LCOV - code coverage report
Current view: top level - gcore - gdalmultidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 4973 5490 90.6 %
Date: 2025-12-01 18:11:08 Functions: 481 556 86.5 %

          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_pam_multidim.h"
      31             : #include "gdal_rat.h"
      32             : #include "gdal_utils.h"
      33             : #include "cpl_safemaths.hpp"
      34             : #include "memmultidim.h"
      35             : #include "ogrsf_frmts.h"
      36             : #include "gdalmultidim_priv.h"
      37             : 
      38             : #if defined(__clang__) || defined(_MSC_VER)
      39             : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
      40             : #endif
      41             : 
      42             : /************************************************************************/
      43             : /*                       GDALMDArrayUnscaled                            */
      44             : /************************************************************************/
      45             : 
      46             : class GDALMDArrayUnscaled final : public GDALPamMDArray
      47             : {
      48             :   private:
      49             :     std::shared_ptr<GDALMDArray> m_poParent{};
      50             :     const GDALExtendedDataType m_dt;
      51             :     bool m_bHasNoData;
      52             :     const double m_dfScale;
      53             :     const double m_dfOffset;
      54             :     std::vector<GByte> m_abyRawNoData{};
      55             : 
      56             :   protected:
      57          13 :     explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
      58             :                                  double dfScale, double dfOffset,
      59             :                                  double dfOverriddenDstNodata, GDALDataType eDT)
      60          26 :         : GDALAbstractMDArray(std::string(),
      61          26 :                               "Unscaled view of " + poParent->GetFullName()),
      62             :           GDALPamMDArray(
      63          26 :               std::string(), "Unscaled view of " + poParent->GetFullName(),
      64          26 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
      65          13 :           m_poParent(std::move(poParent)),
      66             :           m_dt(GDALExtendedDataType::Create(eDT)),
      67          13 :           m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
      68          78 :           m_dfScale(dfScale), m_dfOffset(dfOffset)
      69             :     {
      70          13 :         m_abyRawNoData.resize(m_dt.GetSize());
      71             :         const auto eNonComplexDT =
      72          13 :             GDALGetNonComplexDataType(m_dt.GetNumericDataType());
      73          26 :         GDALCopyWords64(
      74          13 :             &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
      75             :             eNonComplexDT, GDALGetDataTypeSizeBytes(eNonComplexDT),
      76          13 :             GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
      77          13 :     }
      78             : 
      79             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
      80             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      81             :                const GDALExtendedDataType &bufferDataType,
      82             :                void *pDstBuffer) const override;
      83             : 
      84             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
      85             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      86             :                 const GDALExtendedDataType &bufferDataType,
      87             :                 const void *pSrcBuffer) override;
      88             : 
      89           0 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
      90             :                      CSLConstList papszOptions) const override
      91             :     {
      92           0 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
      93             :     }
      94             : 
      95             :   public:
      96             :     static std::shared_ptr<GDALMDArrayUnscaled>
      97          13 :     Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
      98             :            double dfOffset, double dfDstNodata, GDALDataType eDT)
      99             :     {
     100             :         auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
     101          13 :             poParent, dfScale, dfOffset, dfDstNodata, eDT)));
     102          13 :         newAr->SetSelf(newAr);
     103          13 :         return newAr;
     104             :     }
     105             : 
     106           1 :     bool IsWritable() const override
     107             :     {
     108           1 :         return m_poParent->IsWritable();
     109             :     }
     110             : 
     111          15 :     const std::string &GetFilename() const override
     112             :     {
     113          15 :         return m_poParent->GetFilename();
     114             :     }
     115             : 
     116             :     const std::vector<std::shared_ptr<GDALDimension>> &
     117         220 :     GetDimensions() const override
     118             :     {
     119         220 :         return m_poParent->GetDimensions();
     120             :     }
     121             : 
     122         103 :     const GDALExtendedDataType &GetDataType() const override
     123             :     {
     124         103 :         return m_dt;
     125             :     }
     126             : 
     127           1 :     const std::string &GetUnit() const override
     128             :     {
     129           1 :         return m_poParent->GetUnit();
     130             :     }
     131             : 
     132           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
     133             :     {
     134           1 :         return m_poParent->GetSpatialRef();
     135             :     }
     136             : 
     137           6 :     const void *GetRawNoDataValue() const override
     138             :     {
     139           6 :         return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
     140             :     }
     141             : 
     142           1 :     bool SetRawNoDataValue(const void *pRawNoData) override
     143             :     {
     144           1 :         m_bHasNoData = true;
     145           1 :         memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
     146           1 :         return true;
     147             :     }
     148             : 
     149           4 :     std::vector<GUInt64> GetBlockSize() const override
     150             :     {
     151           4 :         return m_poParent->GetBlockSize();
     152             :     }
     153             : 
     154             :     std::shared_ptr<GDALAttribute>
     155           0 :     GetAttribute(const std::string &osName) const override
     156             :     {
     157           0 :         return m_poParent->GetAttribute(osName);
     158             :     }
     159             : 
     160             :     std::vector<std::shared_ptr<GDALAttribute>>
     161           1 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
     162             :     {
     163           1 :         return m_poParent->GetAttributes(papszOptions);
     164             :     }
     165             : 
     166           0 :     bool SetUnit(const std::string &osUnit) override
     167             :     {
     168           0 :         return m_poParent->SetUnit(osUnit);
     169             :     }
     170             : 
     171           0 :     bool SetSpatialRef(const OGRSpatialReference *poSRS) override
     172             :     {
     173           0 :         return m_poParent->SetSpatialRef(poSRS);
     174             :     }
     175             : 
     176             :     std::shared_ptr<GDALAttribute>
     177           1 :     CreateAttribute(const std::string &osName,
     178             :                     const std::vector<GUInt64> &anDimensions,
     179             :                     const GDALExtendedDataType &oDataType,
     180             :                     CSLConstList papszOptions = nullptr) override
     181             :     {
     182           1 :         return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
     183           1 :                                            papszOptions);
     184             :     }
     185             : };
     186             : 
     187             : /************************************************************************/
     188             : /*                         ~GDALIHasAttribute()                         */
     189             : /************************************************************************/
     190             : 
     191             : GDALIHasAttribute::~GDALIHasAttribute() = default;
     192             : 
     193             : /************************************************************************/
     194             : /*                            GetAttribute()                            */
     195             : /************************************************************************/
     196             : 
     197             : /** Return an attribute by its name.
     198             :  *
     199             :  * If the attribute does not exist, nullptr should be silently returned.
     200             :  *
     201             :  * @note Driver implementation: this method will fallback to
     202             :  * GetAttributeFromAttributes() is not explicitly implemented
     203             :  *
     204             :  * Drivers known to implement it for groups and arrays: MEM, netCDF.
     205             :  *
     206             :  * This is the same as the C function GDALGroupGetAttribute() or
     207             :  * GDALMDArrayGetAttribute().
     208             :  *
     209             :  * @param osName Attribute name
     210             :  * @return the attribute, or nullptr if it does not exist or an error occurred.
     211             :  */
     212             : std::shared_ptr<GDALAttribute>
     213        2172 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
     214             : {
     215        2172 :     return GetAttributeFromAttributes(osName);
     216             : }
     217             : 
     218             : /************************************************************************/
     219             : /*                       GetAttributeFromAttributes()                   */
     220             : /************************************************************************/
     221             : 
     222             : /** Possible fallback implementation for GetAttribute() using GetAttributes().
     223             :  */
     224             : std::shared_ptr<GDALAttribute>
     225        2172 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
     226             : {
     227        4344 :     auto attrs(GetAttributes());
     228       14975 :     for (const auto &attr : attrs)
     229             :     {
     230       14287 :         if (attr->GetName() == osName)
     231        1484 :             return attr;
     232             :     }
     233         688 :     return nullptr;
     234             : }
     235             : 
     236             : /************************************************************************/
     237             : /*                           GetAttributes()                            */
     238             : /************************************************************************/
     239             : 
     240             : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
     241             :  *
     242             :  * If the attribute does not exist, nullptr should be silently returned.
     243             :  *
     244             :  * @note Driver implementation: optionally implemented. If implemented,
     245             :  * GetAttribute() should also be implemented.
     246             :  *
     247             :  * Drivers known to implement it for groups and arrays: MEM, netCDF.
     248             :  *
     249             :  * This is the same as the C function GDALGroupGetAttributes() or
     250             :  * GDALMDArrayGetAttributes().
     251             : 
     252             :  * @param papszOptions Driver specific options determining how attributes
     253             :  * should be retrieved. Pass nullptr for default behavior.
     254             :  *
     255             :  * @return the attributes.
     256             :  */
     257             : std::vector<std::shared_ptr<GDALAttribute>>
     258          54 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
     259             : {
     260          54 :     return {};
     261             : }
     262             : 
     263             : /************************************************************************/
     264             : /*                             CreateAttribute()                         */
     265             : /************************************************************************/
     266             : 
     267             : /** Create an attribute within a GDALMDArray or GDALGroup.
     268             :  *
     269             :  * The attribute might not be "physically" created until a value is written
     270             :  * into it.
     271             :  *
     272             :  * Optionally implemented.
     273             :  *
     274             :  * Drivers known to implement it: MEM, netCDF
     275             :  *
     276             :  * This is the same as the C function GDALGroupCreateAttribute() or
     277             :  * GDALMDArrayCreateAttribute()
     278             :  *
     279             :  * @param osName Attribute name.
     280             :  * @param anDimensions List of dimension sizes, ordered from the slowest varying
     281             :  *                     dimension first to the fastest varying dimension last.
     282             :  *                     Empty for a scalar attribute (common case)
     283             :  * @param oDataType  Attribute data type.
     284             :  * @param papszOptions Driver specific options determining how the attribute.
     285             :  * should be created.
     286             :  *
     287             :  * @return the new attribute, or nullptr if case of error
     288             :  */
     289           0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
     290             :     CPL_UNUSED const std::string &osName,
     291             :     CPL_UNUSED const std::vector<GUInt64> &anDimensions,
     292             :     CPL_UNUSED const GDALExtendedDataType &oDataType,
     293             :     CPL_UNUSED CSLConstList papszOptions)
     294             : {
     295           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     296             :              "CreateAttribute() not implemented");
     297           0 :     return nullptr;
     298             : }
     299             : 
     300             : /************************************************************************/
     301             : /*                          DeleteAttribute()                           */
     302             : /************************************************************************/
     303             : 
     304             : /** Delete an attribute from a GDALMDArray or GDALGroup.
     305             :  *
     306             :  * Optionally implemented.
     307             :  *
     308             :  * After this call, if a previously obtained instance of the deleted object
     309             :  * is still alive, no method other than for freeing it should be invoked.
     310             :  *
     311             :  * Drivers known to implement it: MEM, netCDF
     312             :  *
     313             :  * This is the same as the C function GDALGroupDeleteAttribute() or
     314             :  * GDALMDArrayDeleteAttribute()
     315             :  *
     316             :  * @param osName Attribute name.
     317             :  * @param papszOptions Driver specific options determining how the attribute.
     318             :  * should be deleted.
     319             :  *
     320             :  * @return true in case of success
     321             :  * @since GDAL 3.8
     322             :  */
     323           0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
     324             :                                         CPL_UNUSED CSLConstList papszOptions)
     325             : {
     326           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     327             :              "DeleteAttribute() not implemented");
     328           0 :     return false;
     329             : }
     330             : 
     331             : /************************************************************************/
     332             : /*                            GDALGroup()                               */
     333             : /************************************************************************/
     334             : 
     335             : //! @cond Doxygen_Suppress
     336        7360 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
     337        7360 :                      const std::string &osContext)
     338        7360 :     : m_osName(osParentName.empty() ? "/" : osName),
     339             :       m_osFullName(
     340       14720 :           !osParentName.empty()
     341       11335 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
     342             :               : "/"),
     343       18695 :       m_osContext(osContext)
     344             : {
     345        7360 : }
     346             : 
     347             : //! @endcond
     348             : 
     349             : /************************************************************************/
     350             : /*                            ~GDALGroup()                              */
     351             : /************************************************************************/
     352             : 
     353             : GDALGroup::~GDALGroup() = default;
     354             : 
     355             : /************************************************************************/
     356             : /*                          GetMDArrayNames()                           */
     357             : /************************************************************************/
     358             : 
     359             : /** Return the list of multidimensional array names contained in this group.
     360             :  *
     361             :  * @note Driver implementation: optionally implemented. If implemented,
     362             :  * OpenMDArray() should also be implemented.
     363             :  *
     364             :  * Drivers known to implement it: MEM, netCDF.
     365             :  *
     366             :  * This is the same as the C function GDALGroupGetMDArrayNames().
     367             :  *
     368             :  * @param papszOptions Driver specific options determining how arrays
     369             :  * should be retrieved. Pass nullptr for default behavior.
     370             :  *
     371             :  * @return the array names.
     372             :  */
     373             : std::vector<std::string>
     374           0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
     375             : {
     376           0 :     return {};
     377             : }
     378             : 
     379             : /************************************************************************/
     380             : /*                     GetMDArrayFullNamesRecursive()                   */
     381             : /************************************************************************/
     382             : 
     383             : /** Return the list of multidimensional array full names contained in this
     384             :  * group and its subgroups.
     385             :  *
     386             :  * This is the same as the C function GDALGroupGetMDArrayFullNamesRecursive().
     387             :  *
     388             :  * @param papszGroupOptions Driver specific options determining how groups
     389             :  * should be retrieved. Pass nullptr for default behavior.
     390             :  * @param papszArrayOptions Driver specific options determining how arrays
     391             :  * should be retrieved. Pass nullptr for default behavior.
     392             :  *
     393             :  * @return the array full names.
     394             :  *
     395             :  * @since 3.11
     396             :  */
     397             : std::vector<std::string>
     398          10 : GDALGroup::GetMDArrayFullNamesRecursive(CSLConstList papszGroupOptions,
     399             :                                         CSLConstList papszArrayOptions) const
     400             : {
     401          10 :     std::vector<std::string> ret;
     402          20 :     std::list<std::shared_ptr<GDALGroup>> stackGroups;
     403          10 :     stackGroups.push_back(nullptr);  // nullptr means this
     404          23 :     while (!stackGroups.empty())
     405             :     {
     406          26 :         std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
     407          13 :         stackGroups.erase(stackGroups.begin());
     408          13 :         const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
     409          31 :         for (const std::string &arrayName :
     410          75 :              poCurGroup->GetMDArrayNames(papszArrayOptions))
     411             :         {
     412          62 :             std::string osFullName = poCurGroup->GetFullName();
     413          31 :             if (!osFullName.empty() && osFullName.back() != '/')
     414           3 :                 osFullName += '/';
     415          31 :             osFullName += arrayName;
     416          31 :             ret.push_back(std::move(osFullName));
     417             :         }
     418          13 :         auto insertionPoint = stackGroups.begin();
     419           3 :         for (const auto &osSubGroup :
     420          19 :              poCurGroup->GetGroupNames(papszGroupOptions))
     421             :         {
     422           6 :             auto poSubGroup = poCurGroup->OpenGroup(osSubGroup);
     423           3 :             if (poSubGroup)
     424           3 :                 stackGroups.insert(insertionPoint, std::move(poSubGroup));
     425             :         }
     426             :     }
     427             : 
     428          20 :     return ret;
     429             : }
     430             : 
     431             : /************************************************************************/
     432             : /*                            OpenMDArray()                             */
     433             : /************************************************************************/
     434             : 
     435             : /** Open and return a multidimensional array.
     436             :  *
     437             :  * @note Driver implementation: optionally implemented. If implemented,
     438             :  * GetMDArrayNames() should also be implemented.
     439             :  *
     440             :  * Drivers known to implement it: MEM, netCDF.
     441             :  *
     442             :  * This is the same as the C function GDALGroupOpenMDArray().
     443             :  *
     444             :  * @param osName Array name.
     445             :  * @param papszOptions Driver specific options determining how the array should
     446             :  * be opened.  Pass nullptr for default behavior.
     447             :  *
     448             :  * @return the array, or nullptr.
     449             :  */
     450             : std::shared_ptr<GDALMDArray>
     451           0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
     452             :                        CPL_UNUSED CSLConstList papszOptions) const
     453             : {
     454           0 :     return nullptr;
     455             : }
     456             : 
     457             : /************************************************************************/
     458             : /*                           GetGroupNames()                            */
     459             : /************************************************************************/
     460             : 
     461             : /** Return the list of sub-groups contained in this group.
     462             :  *
     463             :  * @note Driver implementation: optionally implemented. If implemented,
     464             :  * OpenGroup() should also be implemented.
     465             :  *
     466             :  * Drivers known to implement it: MEM, netCDF.
     467             :  *
     468             :  * This is the same as the C function GDALGroupGetGroupNames().
     469             :  *
     470             :  * @param papszOptions Driver specific options determining how groups
     471             :  * should be retrieved. Pass nullptr for default behavior.
     472             :  *
     473             :  * @return the group names.
     474             :  */
     475             : std::vector<std::string>
     476           4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
     477             : {
     478           4 :     return {};
     479             : }
     480             : 
     481             : /************************************************************************/
     482             : /*                             OpenGroup()                              */
     483             : /************************************************************************/
     484             : 
     485             : /** Open and return a sub-group.
     486             :  *
     487             :  * @note Driver implementation: optionally implemented. If implemented,
     488             :  * GetGroupNames() should also be implemented.
     489             :  *
     490             :  * Drivers known to implement it: MEM, netCDF.
     491             :  *
     492             :  * This is the same as the C function GDALGroupOpenGroup().
     493             :  *
     494             :  * @param osName Sub-group name.
     495             :  * @param papszOptions Driver specific options determining how the sub-group
     496             :  * should be opened.  Pass nullptr for default behavior.
     497             :  *
     498             :  * @return the group, or nullptr.
     499             :  */
     500             : std::shared_ptr<GDALGroup>
     501           4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
     502             :                      CPL_UNUSED CSLConstList papszOptions) const
     503             : {
     504           4 :     return nullptr;
     505             : }
     506             : 
     507             : /************************************************************************/
     508             : /*                        GetVectorLayerNames()                         */
     509             : /************************************************************************/
     510             : 
     511             : /** Return the list of layer names contained in this group.
     512             :  *
     513             :  * @note Driver implementation: optionally implemented. If implemented,
     514             :  * OpenVectorLayer() should also be implemented.
     515             :  *
     516             :  * Drivers known to implement it: OpenFileGDB, FileGDB
     517             :  *
     518             :  * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
     519             :  * GDALDataset::GetLayer() should then be used.
     520             :  *
     521             :  * This is the same as the C function GDALGroupGetVectorLayerNames().
     522             :  *
     523             :  * @param papszOptions Driver specific options determining how layers
     524             :  * should be retrieved. Pass nullptr for default behavior.
     525             :  *
     526             :  * @return the vector layer names.
     527             :  * @since GDAL 3.4
     528             :  */
     529             : std::vector<std::string>
     530           1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
     531             : {
     532           1 :     return {};
     533             : }
     534             : 
     535             : /************************************************************************/
     536             : /*                           OpenVectorLayer()                          */
     537             : /************************************************************************/
     538             : 
     539             : /** Open and return a vector layer.
     540             :  *
     541             :  * Due to the historical ownership of OGRLayer* by GDALDataset*, the
     542             :  * lifetime of the returned OGRLayer* is linked to the one of the owner
     543             :  * dataset (contrary to the general design of this class where objects can be
     544             :  * used independently of the object that returned them)
     545             :  *
     546             :  * @note Driver implementation: optionally implemented. If implemented,
     547             :  * GetVectorLayerNames() should also be implemented.
     548             :  *
     549             :  * Drivers known to implement it: MEM, netCDF.
     550             :  *
     551             :  * This is the same as the C function GDALGroupOpenVectorLayer().
     552             :  *
     553             :  * @param osName Vector layer name.
     554             :  * @param papszOptions Driver specific options determining how the layer should
     555             :  * be opened.  Pass nullptr for default behavior.
     556             :  *
     557             :  * @return the group, or nullptr.
     558             :  */
     559           2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
     560             :                                      CPL_UNUSED CSLConstList papszOptions) const
     561             : {
     562           2 :     return nullptr;
     563             : }
     564             : 
     565             : /************************************************************************/
     566             : /*                             GetDimensions()                          */
     567             : /************************************************************************/
     568             : 
     569             : /** Return the list of dimensions contained in this group and used by its
     570             :  * arrays.
     571             :  *
     572             :  * This is for dimensions that can potentially be used by several arrays.
     573             :  * Not all drivers might implement this. To retrieve the dimensions used by
     574             :  * a specific array, use GDALMDArray::GetDimensions().
     575             :  *
     576             :  * Drivers known to implement it: MEM, netCDF
     577             :  *
     578             :  * This is the same as the C function GDALGroupGetDimensions().
     579             :  *
     580             :  * @param papszOptions Driver specific options determining how groups
     581             :  * should be retrieved. Pass nullptr for default behavior.
     582             :  *
     583             :  * @return the dimensions.
     584             :  */
     585             : std::vector<std::shared_ptr<GDALDimension>>
     586          11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
     587             : {
     588          11 :     return {};
     589             : }
     590             : 
     591             : /************************************************************************/
     592             : /*                         GetStructuralInfo()                          */
     593             : /************************************************************************/
     594             : 
     595             : /** Return structural information on the group.
     596             :  *
     597             :  * This may be the compression, etc..
     598             :  *
     599             :  * The return value should not be freed and is valid until GDALGroup is
     600             :  * released or this function called again.
     601             :  *
     602             :  * This is the same as the C function GDALGroupGetStructuralInfo().
     603             :  */
     604          42 : CSLConstList GDALGroup::GetStructuralInfo() const
     605             : {
     606          42 :     return nullptr;
     607             : }
     608             : 
     609             : /************************************************************************/
     610             : /*                              CreateGroup()                           */
     611             : /************************************************************************/
     612             : 
     613             : /** Create a sub-group within a group.
     614             :  *
     615             :  * Optionally implemented by drivers.
     616             :  *
     617             :  * Drivers known to implement it: MEM, netCDF
     618             :  *
     619             :  * This is the same as the C function GDALGroupCreateGroup().
     620             :  *
     621             :  * @param osName Sub-group name.
     622             :  * @param papszOptions Driver specific options determining how the sub-group
     623             :  * should be created.
     624             :  *
     625             :  * @return the new sub-group, or nullptr in case of error.
     626             :  */
     627             : std::shared_ptr<GDALGroup>
     628           0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
     629             :                        CPL_UNUSED CSLConstList papszOptions)
     630             : {
     631           0 :     CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
     632           0 :     return nullptr;
     633             : }
     634             : 
     635             : /************************************************************************/
     636             : /*                          DeleteGroup()                               */
     637             : /************************************************************************/
     638             : 
     639             : /** Delete a sub-group from a group.
     640             :  *
     641             :  * Optionally implemented.
     642             :  *
     643             :  * After this call, if a previously obtained instance of the deleted object
     644             :  * is still alive, no method other than for freeing it should be invoked.
     645             :  *
     646             :  * Drivers known to implement it: MEM, Zarr
     647             :  *
     648             :  * This is the same as the C function GDALGroupDeleteGroup().
     649             :  *
     650             :  * @param osName Sub-group name.
     651             :  * @param papszOptions Driver specific options determining how the group.
     652             :  * should be deleted.
     653             :  *
     654             :  * @return true in case of success
     655             :  * @since GDAL 3.8
     656             :  */
     657           0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
     658             :                             CPL_UNUSED CSLConstList papszOptions)
     659             : {
     660           0 :     CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
     661           0 :     return false;
     662             : }
     663             : 
     664             : /************************************************************************/
     665             : /*                            CreateDimension()                         */
     666             : /************************************************************************/
     667             : 
     668             : /** Create a dimension within a group.
     669             :  *
     670             :  * @note Driver implementation: drivers supporting CreateDimension() should
     671             :  * implement this method, but do not have necessarily to implement
     672             :  * GDALGroup::GetDimensions().
     673             :  *
     674             :  * Drivers known to implement it: MEM, netCDF
     675             :  *
     676             :  * This is the same as the C function GDALGroupCreateDimension().
     677             :  *
     678             :  * @param osName Dimension name.
     679             :  * @param osType Dimension type (might be empty, and ignored by drivers)
     680             :  * @param osDirection Dimension direction (might be empty, and ignored by
     681             :  * drivers)
     682             :  * @param nSize  Number of values indexed by this dimension. Should be > 0.
     683             :  * @param papszOptions Driver specific options determining how the dimension
     684             :  * should be created.
     685             :  *
     686             :  * @return the new dimension, or nullptr if case of error
     687             :  */
     688           0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
     689             :     CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
     690             :     CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
     691             :     CPL_UNUSED CSLConstList papszOptions)
     692             : {
     693           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     694             :              "CreateDimension() not implemented");
     695           0 :     return nullptr;
     696             : }
     697             : 
     698             : /************************************************************************/
     699             : /*                             CreateMDArray()                          */
     700             : /************************************************************************/
     701             : 
     702             : /** Create a multidimensional array within a group.
     703             :  *
     704             :  * It is recommended that the GDALDimension objects passed in aoDimensions
     705             :  * belong to this group, either by retrieving them with GetDimensions()
     706             :  * or creating a new one with CreateDimension().
     707             :  *
     708             :  * Optionally implemented.
     709             :  *
     710             :  * Drivers known to implement it: MEM, netCDF
     711             :  *
     712             :  * This is the same as the C function GDALGroupCreateMDArray().
     713             :  *
     714             :  * @note Driver implementation: drivers should take into account the possibility
     715             :  * that GDALDimension object passed in aoDimensions might belong to a different
     716             :  * group / dataset / driver and act accordingly.
     717             :  *
     718             :  * @param osName Array name.
     719             :  * @param aoDimensions List of dimensions, ordered from the slowest varying
     720             :  *                     dimension first to the fastest varying dimension last.
     721             :  *                     Might be empty for a scalar array (if supported by
     722             :  * driver)
     723             :  * @param oDataType  Array data type.
     724             :  * @param papszOptions Driver specific options determining how the array
     725             :  * should be created.
     726             :  *
     727             :  * @return the new array, or nullptr in case of error
     728             :  */
     729           0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
     730             :     CPL_UNUSED const std::string &osName,
     731             :     CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
     732             :     CPL_UNUSED const GDALExtendedDataType &oDataType,
     733             :     CPL_UNUSED CSLConstList papszOptions)
     734             : {
     735           0 :     CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
     736           0 :     return nullptr;
     737             : }
     738             : 
     739             : /************************************************************************/
     740             : /*                          DeleteMDArray()                             */
     741             : /************************************************************************/
     742             : 
     743             : /** Delete an array from a group.
     744             :  *
     745             :  * Optionally implemented.
     746             :  *
     747             :  * After this call, if a previously obtained instance of the deleted object
     748             :  * is still alive, no method other than for freeing it should be invoked.
     749             :  *
     750             :  * Drivers known to implement it: MEM, Zarr
     751             :  *
     752             :  * This is the same as the C function GDALGroupDeleteMDArray().
     753             :  *
     754             :  * @param osName Arrayname.
     755             :  * @param papszOptions Driver specific options determining how the array.
     756             :  * should be deleted.
     757             :  *
     758             :  * @return true in case of success
     759             :  * @since GDAL 3.8
     760             :  */
     761           0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
     762             :                               CPL_UNUSED CSLConstList papszOptions)
     763             : {
     764           0 :     CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
     765           0 :     return false;
     766             : }
     767             : 
     768             : /************************************************************************/
     769             : /*                           GetTotalCopyCost()                         */
     770             : /************************************************************************/
     771             : 
     772             : /** Return a total "cost" to copy the group.
     773             :  *
     774             :  * Used as a parameter for CopFrom()
     775             :  */
     776          33 : GUInt64 GDALGroup::GetTotalCopyCost() const
     777             : {
     778          33 :     GUInt64 nCost = COPY_COST;
     779          33 :     nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
     780             : 
     781          66 :     auto groupNames = GetGroupNames();
     782          39 :     for (const auto &name : groupNames)
     783             :     {
     784          12 :         auto subGroup = OpenGroup(name);
     785           6 :         if (subGroup)
     786             :         {
     787           6 :             nCost += subGroup->GetTotalCopyCost();
     788             :         }
     789             :     }
     790             : 
     791          33 :     auto arrayNames = GetMDArrayNames();
     792         102 :     for (const auto &name : arrayNames)
     793             :     {
     794         138 :         auto array = OpenMDArray(name);
     795          69 :         if (array)
     796             :         {
     797          69 :             nCost += array->GetTotalCopyCost();
     798             :         }
     799             :     }
     800          66 :     return nCost;
     801             : }
     802             : 
     803             : /************************************************************************/
     804             : /*                               CopyFrom()                             */
     805             : /************************************************************************/
     806             : 
     807             : /** Copy the content of a group into a new (generally empty) group.
     808             :  *
     809             :  * @param poDstRootGroup Destination root group. Must NOT be nullptr.
     810             :  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
     811             :  *                   of some output drivers this is not recommended)
     812             :  * @param poSrcGroup Source group. Must NOT be nullptr.
     813             :  * @param bStrict Whether to enable strict mode. In strict mode, any error will
     814             :  *                stop the copy. In relaxed mode, the copy will be attempted to
     815             :  *                be pursued.
     816             :  * @param nCurCost  Should be provided as a variable initially set to 0.
     817             :  * @param nTotalCost Total cost from GetTotalCopyCost().
     818             :  * @param pfnProgress Progress callback, or nullptr.
     819             :  * @param pProgressData Progress user data, or nulptr.
     820             :  * @param papszOptions Creation options. Currently, only array creation
     821             :  *                     options are supported. They must be prefixed with
     822             :  * "ARRAY:" . The scope may be further restricted to arrays of a certain
     823             :  *                     dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
     824             :  *                     For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
     825             :  *                     restrict BLOCKSIZE=256,256 to arrays of dimension 2.
     826             :  *                     Restriction to arrays of a given name is done with adding
     827             :  *                     "IF(NAME={name}):" after "ARRAY:". {name} can also be
     828             :  *                     a full qualified name.
     829             :  *                     A non-driver specific ARRAY option, "AUTOSCALE=YES" can
     830             :  * be used to ask (non indexing) variables of type Float32 or Float64 to be
     831             :  * scaled to UInt16 with scale and offset values being computed from the minimum
     832             :  * and maximum of the source array. The integer data type used can be set with
     833             :  *                     AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
     834             :  *
     835             :  * @return true in case of success (or partial success if bStrict == false).
     836             :  */
     837          33 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
     838             :                          GDALDataset *poSrcDS,
     839             :                          const std::shared_ptr<GDALGroup> &poSrcGroup,
     840             :                          bool bStrict, GUInt64 &nCurCost,
     841             :                          const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
     842             :                          void *pProgressData, CSLConstList papszOptions)
     843             : {
     844          33 :     if (pfnProgress == nullptr)
     845           0 :         pfnProgress = GDALDummyProgress;
     846             : 
     847             : #define EXIT_OR_CONTINUE_IF_NULL(x)                                            \
     848             :     if (!(x))                                                                  \
     849             :     {                                                                          \
     850             :         if (bStrict)                                                           \
     851             :             return false;                                                      \
     852             :         continue;                                                              \
     853             :     }                                                                          \
     854             :     (void)0
     855             : 
     856             :     try
     857             :     {
     858          33 :         nCurCost += GDALGroup::COPY_COST;
     859             : 
     860          66 :         const auto srcDims = poSrcGroup->GetDimensions();
     861             :         std::map<std::string, std::shared_ptr<GDALDimension>>
     862          66 :             mapExistingDstDims;
     863          66 :         std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
     864          87 :         for (const auto &dim : srcDims)
     865             :         {
     866             :             auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
     867          54 :                                           dim->GetDirection(), dim->GetSize());
     868          54 :             EXIT_OR_CONTINUE_IF_NULL(dstDim);
     869          54 :             mapExistingDstDims[dim->GetName()] = std::move(dstDim);
     870         108 :             auto poIndexingVarSrc(dim->GetIndexingVariable());
     871          54 :             if (poIndexingVarSrc)
     872             :             {
     873             :                 mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
     874          35 :                                                        ->GetName()] =
     875          70 :                     dim->GetName();
     876             :             }
     877             :         }
     878             : 
     879          66 :         auto attrs = poSrcGroup->GetAttributes();
     880          51 :         for (const auto &attr : attrs)
     881             :         {
     882             :             auto dstAttr =
     883          18 :                 CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
     884          36 :                                 attr->GetDataType());
     885          18 :             EXIT_OR_CONTINUE_IF_NULL(dstAttr);
     886          18 :             auto raw(attr->ReadAsRaw());
     887          18 :             if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
     888           0 :                 return false;
     889             :         }
     890          33 :         if (!attrs.empty())
     891             :         {
     892           8 :             nCurCost += attrs.size() * GDALAttribute::COPY_COST;
     893           8 :             if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
     894           0 :                 return false;
     895             :         }
     896             : 
     897             :         const auto CopyArray =
     898          69 :             [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
     899             :              &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
     900             :              papszOptions, bStrict, &nCurCost,
     901         616 :              nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
     902             :         {
     903             :             // Map source dimensions to target dimensions
     904         138 :             std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
     905          69 :             const auto &srcArrayDims(srcArray->GetDimensions());
     906         172 :             for (const auto &dim : srcArrayDims)
     907             :             {
     908             :                 auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
     909         103 :                     dim->GetFullName());
     910         103 :                 if (dstDim && dstDim->GetSize() == dim->GetSize())
     911             :                 {
     912          93 :                     dstArrayDims.emplace_back(dstDim);
     913             :                 }
     914             :                 else
     915             :                 {
     916          10 :                     auto oIter = mapExistingDstDims.find(dim->GetName());
     917          19 :                     if (oIter != mapExistingDstDims.end() &&
     918           9 :                         oIter->second->GetSize() == dim->GetSize())
     919             :                     {
     920           8 :                         dstArrayDims.emplace_back(oIter->second);
     921             :                     }
     922             :                     else
     923             :                     {
     924           2 :                         std::string newDimName;
     925           2 :                         if (oIter == mapExistingDstDims.end())
     926             :                         {
     927           1 :                             newDimName = dim->GetName();
     928             :                         }
     929             :                         else
     930             :                         {
     931           1 :                             std::string newDimNamePrefix(srcArray->GetName() +
     932           3 :                                                          '_' + dim->GetName());
     933           1 :                             newDimName = newDimNamePrefix;
     934           1 :                             int nIterCount = 2;
     935           0 :                             while (
     936           1 :                                 cpl::contains(mapExistingDstDims, newDimName))
     937             :                             {
     938           0 :                                 newDimName = newDimNamePrefix +
     939           0 :                                              CPLSPrintf("_%d", nIterCount);
     940           0 :                                 nIterCount++;
     941             :                             }
     942             :                         }
     943           4 :                         dstDim = CreateDimension(newDimName, dim->GetType(),
     944             :                                                  dim->GetDirection(),
     945           4 :                                                  dim->GetSize());
     946           2 :                         if (!dstDim)
     947           0 :                             return false;
     948           2 :                         mapExistingDstDims[newDimName] = dstDim;
     949           2 :                         dstArrayDims.emplace_back(dstDim);
     950             :                     }
     951             :                 }
     952             :             }
     953             : 
     954         138 :             CPLStringList aosArrayCO;
     955          69 :             bool bAutoScale = false;
     956          69 :             GDALDataType eAutoScaleType = GDT_UInt16;
     957          76 :             for (const char *pszItem : cpl::Iterate(papszOptions))
     958             :             {
     959           7 :                 if (STARTS_WITH_CI(pszItem, "ARRAY:"))
     960             :                 {
     961           7 :                     const char *pszOption = pszItem + strlen("ARRAY:");
     962           7 :                     if (STARTS_WITH_CI(pszOption, "IF(DIM="))
     963             :                     {
     964           1 :                         const char *pszNext = strchr(pszOption, ':');
     965           1 :                         if (pszNext != nullptr)
     966             :                         {
     967           1 :                             int nDim = atoi(pszOption + strlen("IF(DIM="));
     968           1 :                             if (static_cast<size_t>(nDim) ==
     969           1 :                                 dstArrayDims.size())
     970             :                             {
     971           1 :                                 pszOption = pszNext + 1;
     972             :                             }
     973             :                             else
     974             :                             {
     975           0 :                                 pszOption = nullptr;
     976             :                             }
     977             :                         }
     978             :                     }
     979           6 :                     else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
     980             :                     {
     981           2 :                         const char *pszName = pszOption + strlen("IF(NAME=");
     982           2 :                         const char *pszNext = strchr(pszName, ':');
     983           2 :                         if (pszNext != nullptr && pszNext > pszName &&
     984           2 :                             pszNext[-1] == ')')
     985             :                         {
     986           4 :                             CPLString osName;
     987           2 :                             osName.assign(pszName, pszNext - pszName - 1);
     988           3 :                             if (osName == srcArray->GetName() ||
     989           1 :                                 osName == srcArray->GetFullName())
     990             :                             {
     991           2 :                                 pszOption = pszNext + 1;
     992             :                             }
     993             :                             else
     994             :                             {
     995           0 :                                 pszOption = nullptr;
     996             :                             }
     997             :                         }
     998             :                     }
     999           7 :                     if (pszOption)
    1000             :                     {
    1001           7 :                         if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
    1002             :                         {
    1003             :                             bAutoScale =
    1004           2 :                                 CPLTestBool(pszOption + strlen("AUTOSCALE="));
    1005             :                         }
    1006           5 :                         else if (STARTS_WITH_CI(pszOption,
    1007             :                                                 "AUTOSCALE_DATA_TYPE="))
    1008             :                         {
    1009           1 :                             const char *pszDataType =
    1010             :                                 pszOption + strlen("AUTOSCALE_DATA_TYPE=");
    1011           1 :                             eAutoScaleType = GDALGetDataTypeByName(pszDataType);
    1012           2 :                             if (GDALDataTypeIsComplex(eAutoScaleType) ||
    1013           1 :                                 GDALDataTypeIsFloating(eAutoScaleType))
    1014             :                             {
    1015           0 :                                 CPLError(CE_Failure, CPLE_NotSupported,
    1016             :                                          "Unsupported value for "
    1017             :                                          "AUTOSCALE_DATA_TYPE");
    1018           0 :                                 return false;
    1019             :                             }
    1020             :                         }
    1021             :                         else
    1022             :                         {
    1023           4 :                             aosArrayCO.AddString(pszOption);
    1024             :                         }
    1025             :                     }
    1026             :                 }
    1027             :             }
    1028             : 
    1029          69 :             if (aosArrayCO.FetchNameValue("BLOCKSIZE") == nullptr)
    1030             :             {
    1031         136 :                 const auto anBlockSize = srcArray->GetBlockSize();
    1032         136 :                 std::string osBlockSize;
    1033          74 :                 for (auto v : anBlockSize)
    1034             :                 {
    1035          69 :                     if (v == 0)
    1036             :                     {
    1037          63 :                         osBlockSize.clear();
    1038          63 :                         break;
    1039             :                     }
    1040           6 :                     if (!osBlockSize.empty())
    1041           2 :                         osBlockSize += ',';
    1042           6 :                     osBlockSize += std::to_string(v);
    1043             :                 }
    1044          68 :                 if (!osBlockSize.empty())
    1045           3 :                     aosArrayCO.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
    1046             :             }
    1047             : 
    1048             :             auto oIterDimName =
    1049          69 :                 mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
    1050          69 :             const auto &srcArrayType = srcArray->GetDataType();
    1051             : 
    1052          69 :             std::shared_ptr<GDALMDArray> dstArray;
    1053             : 
    1054             :             // Only autoscale non-indexing variables
    1055          69 :             bool bHasOffset = false;
    1056          69 :             bool bHasScale = false;
    1057           4 :             if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
    1058           4 :                 (srcArrayType.GetNumericDataType() == GDT_Float16 ||
    1059           2 :                  srcArrayType.GetNumericDataType() == GDT_Float32 ||
    1060           0 :                  srcArrayType.GetNumericDataType() == GDT_Float64) &&
    1061           2 :                 srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
    1062          73 :                 srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
    1063          71 :                 oIterDimName == mapSrcVariableNameToIndexedDimName.end())
    1064             :             {
    1065           2 :                 constexpr bool bApproxOK = false;
    1066           2 :                 constexpr bool bForce = true;
    1067           2 :                 double dfMin = 0.0;
    1068           2 :                 double dfMax = 0.0;
    1069           2 :                 if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
    1070             :                                             nullptr, nullptr, nullptr, nullptr,
    1071           2 :                                             nullptr) != CE_None)
    1072             :                 {
    1073           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1074             :                              "Could not retrieve statistics for array %s",
    1075           0 :                              srcArray->GetName().c_str());
    1076           0 :                     return false;
    1077             :                 }
    1078           2 :                 double dfDTMin = 0;
    1079           2 :                 double dfDTMax = 0;
    1080             : #define setDTMinMax(ctype)                                                     \
    1081             :     do                                                                         \
    1082             :     {                                                                          \
    1083             :         dfDTMin = static_cast<double>(cpl::NumericLimits<ctype>::lowest());    \
    1084             :         dfDTMax = static_cast<double>(cpl::NumericLimits<ctype>::max());       \
    1085             :     } while (0)
    1086             : 
    1087           2 :                 switch (eAutoScaleType)
    1088             :                 {
    1089           0 :                     case GDT_UInt8:
    1090           0 :                         setDTMinMax(GByte);
    1091           0 :                         break;
    1092           0 :                     case GDT_Int8:
    1093           0 :                         setDTMinMax(GInt8);
    1094           0 :                         break;
    1095           1 :                     case GDT_UInt16:
    1096           1 :                         setDTMinMax(GUInt16);
    1097           1 :                         break;
    1098           1 :                     case GDT_Int16:
    1099           1 :                         setDTMinMax(GInt16);
    1100           1 :                         break;
    1101           0 :                     case GDT_UInt32:
    1102           0 :                         setDTMinMax(GUInt32);
    1103           0 :                         break;
    1104           0 :                     case GDT_Int32:
    1105           0 :                         setDTMinMax(GInt32);
    1106           0 :                         break;
    1107           0 :                     case GDT_UInt64:
    1108           0 :                         setDTMinMax(std::uint64_t);
    1109           0 :                         break;
    1110           0 :                     case GDT_Int64:
    1111           0 :                         setDTMinMax(std::int64_t);
    1112           0 :                         break;
    1113           0 :                     case GDT_Float16:
    1114             :                     case GDT_Float32:
    1115             :                     case GDT_Float64:
    1116             :                     case GDT_Unknown:
    1117             :                     case GDT_CInt16:
    1118             :                     case GDT_CInt32:
    1119             :                     case GDT_CFloat16:
    1120             :                     case GDT_CFloat32:
    1121             :                     case GDT_CFloat64:
    1122             :                     case GDT_TypeCount:
    1123           0 :                         CPLAssert(false);
    1124             :                 }
    1125             : 
    1126             :                 dstArray =
    1127           4 :                     CreateMDArray(srcArray->GetName(), dstArrayDims,
    1128           4 :                                   GDALExtendedDataType::Create(eAutoScaleType),
    1129           4 :                                   aosArrayCO.List());
    1130           2 :                 if (!dstArray)
    1131           0 :                     return !bStrict;
    1132             : 
    1133           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1134             :                 {
    1135             :                     // If there's a nodata value in the source array, reserve
    1136             :                     // DTMax for that purpose in the target scaled array
    1137           1 :                     if (!dstArray->SetNoDataValue(dfDTMax))
    1138             :                     {
    1139           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    1140             :                                  "Cannot set nodata value");
    1141           0 :                         return false;
    1142             :                     }
    1143           1 :                     dfDTMax--;
    1144             :                 }
    1145           2 :                 const double dfScale =
    1146           2 :                     dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
    1147           2 :                 const double dfOffset = dfMin - dfDTMin * dfScale;
    1148             : 
    1149           4 :                 if (!dstArray->SetOffset(dfOffset) ||
    1150           2 :                     !dstArray->SetScale(dfScale))
    1151             :                 {
    1152           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1153             :                              "Cannot set scale/offset");
    1154           0 :                     return false;
    1155             :                 }
    1156             : 
    1157           2 :                 auto poUnscaled = dstArray->GetUnscaled();
    1158           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1159             :                 {
    1160           1 :                     poUnscaled->SetNoDataValue(
    1161             :                         srcArray->GetNoDataValueAsDouble());
    1162             :                 }
    1163             : 
    1164             :                 // Copy source array into unscaled array
    1165           4 :                 if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1166             :                                           nCurCost, nTotalCost, pfnProgress,
    1167           2 :                                           pProgressData))
    1168             :                 {
    1169           0 :                     return false;
    1170             :                 }
    1171             :             }
    1172             :             else
    1173             :             {
    1174         134 :                 dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
    1175         134 :                                          srcArrayType, aosArrayCO.List());
    1176          67 :                 if (!dstArray)
    1177           0 :                     return !bStrict;
    1178             : 
    1179         134 :                 if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1180             :                                         nCurCost, nTotalCost, pfnProgress,
    1181          67 :                                         pProgressData))
    1182             :                 {
    1183           0 :                     return false;
    1184             :                 }
    1185             :             }
    1186             : 
    1187             :             // If this array is the indexing variable of a dimension, link them
    1188             :             // together.
    1189          69 :             if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
    1190             :             {
    1191             :                 auto oCorrespondingDimIter =
    1192          35 :                     mapExistingDstDims.find(oIterDimName->second);
    1193          35 :                 if (oCorrespondingDimIter != mapExistingDstDims.end())
    1194             :                 {
    1195             :                     CPLErrorStateBackuper oErrorStateBackuper(
    1196          35 :                         CPLQuietErrorHandler);
    1197          70 :                     oCorrespondingDimIter->second->SetIndexingVariable(
    1198          35 :                         std::move(dstArray));
    1199             :                 }
    1200             :             }
    1201             : 
    1202          69 :             return true;
    1203          33 :         };
    1204             : 
    1205          66 :         const auto arrayNames = poSrcGroup->GetMDArrayNames();
    1206             : 
    1207             :         // Start by copying arrays that are indexing variables of dimensions
    1208         102 :         for (const auto &name : arrayNames)
    1209             :         {
    1210          69 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1211          69 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1212             : 
    1213          69 :             if (cpl::contains(mapSrcVariableNameToIndexedDimName,
    1214          69 :                               srcArray->GetName()))
    1215             :             {
    1216          35 :                 if (!CopyArray(srcArray))
    1217           0 :                     return false;
    1218             :             }
    1219             :         }
    1220             : 
    1221             :         // Then copy regular arrays
    1222         102 :         for (const auto &name : arrayNames)
    1223             :         {
    1224          69 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1225          69 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1226             : 
    1227          69 :             if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
    1228          69 :                                srcArray->GetName()))
    1229             :             {
    1230          34 :                 if (!CopyArray(srcArray))
    1231           0 :                     return false;
    1232             :             }
    1233             :         }
    1234             : 
    1235          66 :         const auto groupNames = poSrcGroup->GetGroupNames();
    1236          39 :         for (const auto &name : groupNames)
    1237             :         {
    1238           6 :             auto srcSubGroup = poSrcGroup->OpenGroup(name);
    1239           6 :             EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
    1240           6 :             auto dstSubGroup = CreateGroup(name);
    1241           6 :             EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
    1242          12 :             if (!dstSubGroup->CopyFrom(
    1243             :                     poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
    1244           6 :                     nTotalCost, pfnProgress, pProgressData, papszOptions))
    1245           0 :                 return false;
    1246             :         }
    1247             : 
    1248          33 :         if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    1249           0 :             return false;
    1250             : 
    1251          33 :         return true;
    1252             :     }
    1253           0 :     catch (const std::exception &e)
    1254             :     {
    1255           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1256           0 :         return false;
    1257             :     }
    1258             : }
    1259             : 
    1260             : /************************************************************************/
    1261             : /*                         GetInnerMostGroup()                          */
    1262             : /************************************************************************/
    1263             : 
    1264             : //! @cond Doxygen_Suppress
    1265             : const GDALGroup *
    1266        1511 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
    1267             :                              std::shared_ptr<GDALGroup> &curGroupHolder,
    1268             :                              std::string &osLastPart) const
    1269             : {
    1270        1511 :     if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
    1271           6 :         return nullptr;
    1272        1505 :     const GDALGroup *poCurGroup = this;
    1273             :     CPLStringList aosTokens(
    1274        3010 :         CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
    1275        1505 :     if (aosTokens.size() == 0)
    1276             :     {
    1277           0 :         return nullptr;
    1278             :     }
    1279             : 
    1280        1843 :     for (int i = 0; i < aosTokens.size() - 1; i++)
    1281             :     {
    1282         350 :         curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
    1283         350 :         if (!curGroupHolder)
    1284             :         {
    1285          12 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
    1286             :                      aosTokens[i]);
    1287          12 :             return nullptr;
    1288             :         }
    1289         338 :         poCurGroup = curGroupHolder.get();
    1290             :     }
    1291        1493 :     osLastPart = aosTokens[aosTokens.size() - 1];
    1292        1493 :     return poCurGroup;
    1293             : }
    1294             : 
    1295             : //! @endcond
    1296             : 
    1297             : /************************************************************************/
    1298             : /*                      OpenMDArrayFromFullname()                       */
    1299             : /************************************************************************/
    1300             : 
    1301             : /** Get an array from its fully qualified name */
    1302             : std::shared_ptr<GDALMDArray>
    1303         731 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
    1304             :                                    CSLConstList papszOptions) const
    1305             : {
    1306        1462 :     std::string osName;
    1307         731 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1308         731 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1309         731 :     if (poGroup == nullptr)
    1310          12 :         return nullptr;
    1311         719 :     return poGroup->OpenMDArray(osName, papszOptions);
    1312             : }
    1313             : 
    1314             : /************************************************************************/
    1315             : /*                      OpenAttributeFromFullname()                     */
    1316             : /************************************************************************/
    1317             : 
    1318             : /** Get an attribute from its fully qualified name */
    1319             : std::shared_ptr<GDALAttribute>
    1320           9 : GDALGroup::OpenAttributeFromFullname(const std::string &osFullName,
    1321             :                                      CSLConstList papszOptions) const
    1322             : {
    1323           9 :     const auto pos = osFullName.rfind('/');
    1324           9 :     if (pos == std::string::npos)
    1325           0 :         return nullptr;
    1326          18 :     const std::string attrName = osFullName.substr(pos + 1);
    1327           9 :     if (pos == 0)
    1328           2 :         return GetAttribute(attrName);
    1329          14 :     const std::string container = osFullName.substr(0, pos);
    1330          14 :     auto poArray = OpenMDArrayFromFullname(container, papszOptions);
    1331           7 :     if (poArray)
    1332           4 :         return poArray->GetAttribute(attrName);
    1333           6 :     auto poGroup = OpenGroupFromFullname(container, papszOptions);
    1334           3 :     if (poGroup)
    1335           1 :         return poGroup->GetAttribute(attrName);
    1336           2 :     return nullptr;
    1337             : }
    1338             : 
    1339             : /************************************************************************/
    1340             : /*                          ResolveMDArray()                            */
    1341             : /************************************************************************/
    1342             : 
    1343             : /** Locate an array in a group and its subgroups by name.
    1344             :  *
    1345             :  * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
    1346             :  * used
    1347             :  * Otherwise the search will start from the group identified by osStartingPath,
    1348             :  * and an array whose name is osName will be looked for in this group (if
    1349             :  * osStartingPath is empty or "/", then the current group is used). If there
    1350             :  * is no match, then a recursive descendant search will be made in its
    1351             :  * subgroups. If there is no match in the subgroups, then the parent (if
    1352             :  * existing) of the group pointed by osStartingPath will be used as the new
    1353             :  * starting point for the search.
    1354             :  *
    1355             :  * @param osName name, qualified or not
    1356             :  * @param osStartingPath fully qualified name of the (sub-)group from which
    1357             :  *                       the search should be started. If this is a non-empty
    1358             :  *                       string, the group on which this method is called should
    1359             :  *                       nominally be the root group (otherwise the path will
    1360             :  *                       be interpreted as from the current group)
    1361             :  * @param papszOptions options to pass to OpenMDArray()
    1362             :  * @since GDAL 3.2
    1363             :  */
    1364             : std::shared_ptr<GDALMDArray>
    1365          19 : GDALGroup::ResolveMDArray(const std::string &osName,
    1366             :                           const std::string &osStartingPath,
    1367             :                           CSLConstList papszOptions) const
    1368             : {
    1369          19 :     if (!osName.empty() && osName[0] == '/')
    1370             :     {
    1371           1 :         auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
    1372           1 :         if (poArray)
    1373           1 :             return poArray;
    1374             :     }
    1375          36 :     std::string osPath(osStartingPath);
    1376          36 :     std::set<std::string> oSetAlreadyVisited;
    1377             : 
    1378             :     while (true)
    1379             :     {
    1380           0 :         std::shared_ptr<GDALGroup> curGroupHolder;
    1381           0 :         std::shared_ptr<GDALGroup> poGroup;
    1382             : 
    1383          22 :         std::queue<std::shared_ptr<GDALGroup>> oQueue;
    1384          22 :         bool goOn = false;
    1385          22 :         if (osPath.empty() || osPath == "/")
    1386             :         {
    1387          11 :             goOn = true;
    1388             :         }
    1389             :         else
    1390             :         {
    1391          22 :             std::string osLastPart;
    1392             :             const GDALGroup *poGroupPtr =
    1393          11 :                 GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
    1394          11 :             if (poGroupPtr)
    1395          11 :                 poGroup = poGroupPtr->OpenGroup(osLastPart);
    1396          22 :             if (poGroup &&
    1397          22 :                 !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
    1398             :             {
    1399          11 :                 oQueue.push(poGroup);
    1400          11 :                 goOn = true;
    1401             :             }
    1402             :         }
    1403             : 
    1404          22 :         if (goOn)
    1405             :         {
    1406          17 :             do
    1407             :             {
    1408             :                 const GDALGroup *groupPtr;
    1409          39 :                 if (!oQueue.empty())
    1410             :                 {
    1411          28 :                     poGroup = oQueue.front();
    1412          28 :                     oQueue.pop();
    1413          28 :                     groupPtr = poGroup.get();
    1414             :                 }
    1415             :                 else
    1416             :                 {
    1417          11 :                     groupPtr = this;
    1418             :                 }
    1419             : 
    1420          39 :                 auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
    1421          39 :                 if (poArray)
    1422          16 :                     return poArray;
    1423             : 
    1424          46 :                 const auto aosGroupNames = groupPtr->GetGroupNames();
    1425          47 :                 for (const auto &osGroupName : aosGroupNames)
    1426             :                 {
    1427          48 :                     auto poSubGroup = groupPtr->OpenGroup(osGroupName);
    1428          48 :                     if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
    1429          48 :                                                      poSubGroup->GetFullName()))
    1430             :                     {
    1431          24 :                         oQueue.push(poSubGroup);
    1432          24 :                         oSetAlreadyVisited.insert(poSubGroup->GetFullName());
    1433             :                     }
    1434             :                 }
    1435          23 :             } while (!oQueue.empty());
    1436             :         }
    1437             : 
    1438           6 :         if (osPath.empty() || osPath == "/")
    1439           2 :             break;
    1440             : 
    1441           4 :         const auto nPos = osPath.rfind('/');
    1442           4 :         if (nPos == 0)
    1443           1 :             osPath = "/";
    1444             :         else
    1445             :         {
    1446           3 :             if (nPos == std::string::npos)
    1447           0 :                 break;
    1448           3 :             osPath.resize(nPos);
    1449             :         }
    1450           4 :     }
    1451           2 :     return nullptr;
    1452             : }
    1453             : 
    1454             : /************************************************************************/
    1455             : /*                       OpenGroupFromFullname()                        */
    1456             : /************************************************************************/
    1457             : 
    1458             : /** Get a group from its fully qualified name.
    1459             :  * @since GDAL 3.2
    1460             :  */
    1461             : std::shared_ptr<GDALGroup>
    1462         564 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
    1463             :                                  CSLConstList papszOptions) const
    1464             : {
    1465        1128 :     std::string osName;
    1466         564 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1467         564 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1468         564 :     if (poGroup == nullptr)
    1469           4 :         return nullptr;
    1470         560 :     return poGroup->OpenGroup(osName, papszOptions);
    1471             : }
    1472             : 
    1473             : /************************************************************************/
    1474             : /*                      OpenDimensionFromFullname()                     */
    1475             : /************************************************************************/
    1476             : 
    1477             : /** Get a dimension from its fully qualified name */
    1478             : std::shared_ptr<GDALDimension>
    1479         205 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
    1480             : {
    1481         410 :     std::string osName;
    1482         205 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1483         205 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1484         205 :     if (poGroup == nullptr)
    1485           2 :         return nullptr;
    1486         406 :     auto dims(poGroup->GetDimensions());
    1487         348 :     for (auto &dim : dims)
    1488             :     {
    1489         296 :         if (dim->GetName() == osName)
    1490         151 :             return dim;
    1491             :     }
    1492          52 :     return nullptr;
    1493             : }
    1494             : 
    1495             : /************************************************************************/
    1496             : /*                           ClearStatistics()                          */
    1497             : /************************************************************************/
    1498             : 
    1499             : /**
    1500             :  * \brief Clear statistics.
    1501             :  *
    1502             :  * @since GDAL 3.4
    1503             :  */
    1504           0 : void GDALGroup::ClearStatistics()
    1505             : {
    1506           0 :     auto groupNames = GetGroupNames();
    1507           0 :     for (const auto &name : groupNames)
    1508             :     {
    1509           0 :         auto subGroup = OpenGroup(name);
    1510           0 :         if (subGroup)
    1511             :         {
    1512           0 :             subGroup->ClearStatistics();
    1513             :         }
    1514             :     }
    1515             : 
    1516           0 :     auto arrayNames = GetMDArrayNames();
    1517           0 :     for (const auto &name : arrayNames)
    1518             :     {
    1519           0 :         auto array = OpenMDArray(name);
    1520           0 :         if (array)
    1521             :         {
    1522           0 :             array->ClearStatistics();
    1523             :         }
    1524             :     }
    1525           0 : }
    1526             : 
    1527             : /************************************************************************/
    1528             : /*                            Rename()                                  */
    1529             : /************************************************************************/
    1530             : 
    1531             : /** Rename the group.
    1532             :  *
    1533             :  * This is not implemented by all drivers.
    1534             :  *
    1535             :  * Drivers known to implement it: MEM, netCDF, ZARR.
    1536             :  *
    1537             :  * This is the same as the C function GDALGroupRename().
    1538             :  *
    1539             :  * @param osNewName New name.
    1540             :  *
    1541             :  * @return true in case of success
    1542             :  * @since GDAL 3.8
    1543             :  */
    1544           0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
    1545             : {
    1546           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
    1547           0 :     return false;
    1548             : }
    1549             : 
    1550             : /************************************************************************/
    1551             : /*                         BaseRename()                                 */
    1552             : /************************************************************************/
    1553             : 
    1554             : //! @cond Doxygen_Suppress
    1555           8 : void GDALGroup::BaseRename(const std::string &osNewName)
    1556             : {
    1557           8 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
    1558           8 :     m_osFullName += osNewName;
    1559           8 :     m_osName = osNewName;
    1560             : 
    1561           8 :     NotifyChildrenOfRenaming();
    1562           8 : }
    1563             : 
    1564             : //! @endcond
    1565             : 
    1566             : /************************************************************************/
    1567             : /*                        ParentRenamed()                               */
    1568             : /************************************************************************/
    1569             : 
    1570             : //! @cond Doxygen_Suppress
    1571           7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
    1572             : {
    1573           7 :     m_osFullName = osNewParentFullName;
    1574           7 :     m_osFullName += "/";
    1575           7 :     m_osFullName += m_osName;
    1576             : 
    1577           7 :     NotifyChildrenOfRenaming();
    1578           7 : }
    1579             : 
    1580             : //! @endcond
    1581             : 
    1582             : /************************************************************************/
    1583             : /*                             Deleted()                                */
    1584             : /************************************************************************/
    1585             : 
    1586             : //! @cond Doxygen_Suppress
    1587          22 : void GDALGroup::Deleted()
    1588             : {
    1589          22 :     m_bValid = false;
    1590             : 
    1591          22 :     NotifyChildrenOfDeletion();
    1592          22 : }
    1593             : 
    1594             : //! @endcond
    1595             : 
    1596             : /************************************************************************/
    1597             : /*                        ParentDeleted()                               */
    1598             : /************************************************************************/
    1599             : 
    1600             : //! @cond Doxygen_Suppress
    1601           3 : void GDALGroup::ParentDeleted()
    1602             : {
    1603           3 :     Deleted();
    1604           3 : }
    1605             : 
    1606             : //! @endcond
    1607             : 
    1608             : /************************************************************************/
    1609             : /*                     CheckValidAndErrorOutIfNot()                     */
    1610             : /************************************************************************/
    1611             : 
    1612             : //! @cond Doxygen_Suppress
    1613       17292 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
    1614             : {
    1615       17292 :     if (!m_bValid)
    1616             :     {
    1617          14 :         CPLError(CE_Failure, CPLE_AppDefined,
    1618             :                  "This object has been deleted. No action on it is possible");
    1619             :     }
    1620       17292 :     return m_bValid;
    1621             : }
    1622             : 
    1623             : //! @endcond
    1624             : 
    1625             : /************************************************************************/
    1626             : /*                       ~GDALAbstractMDArray()                         */
    1627             : /************************************************************************/
    1628             : 
    1629             : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
    1630             : 
    1631             : /************************************************************************/
    1632             : /*                        GDALAbstractMDArray()                         */
    1633             : /************************************************************************/
    1634             : 
    1635             : //! @cond Doxygen_Suppress
    1636       36772 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
    1637       36772 :                                          const std::string &osName)
    1638             :     : m_osName(osName),
    1639             :       m_osFullName(
    1640       36772 :           !osParentName.empty()
    1641       71615 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
    1642      145159 :               : osName)
    1643             : {
    1644       36772 : }
    1645             : 
    1646             : //! @endcond
    1647             : 
    1648             : /************************************************************************/
    1649             : /*                           GetDimensions()                            */
    1650             : /************************************************************************/
    1651             : 
    1652             : /** \fn GDALAbstractMDArray::GetDimensions() const
    1653             :  * \brief Return the dimensions of an attribute/array.
    1654             :  *
    1655             :  * This is the same as the C functions GDALMDArrayGetDimensions() and
    1656             :  * similar to GDALAttributeGetDimensionsSize().
    1657             :  */
    1658             : 
    1659             : /************************************************************************/
    1660             : /*                           GetDataType()                              */
    1661             : /************************************************************************/
    1662             : 
    1663             : /** \fn GDALAbstractMDArray::GetDataType() const
    1664             :  * \brief Return the data type of an attribute/array.
    1665             :  *
    1666             :  * This is the same as the C functions GDALMDArrayGetDataType() and
    1667             :  * GDALAttributeGetDataType()
    1668             :  */
    1669             : 
    1670             : /************************************************************************/
    1671             : /*                        GetDimensionCount()                           */
    1672             : /************************************************************************/
    1673             : 
    1674             : /** Return the number of dimensions.
    1675             :  *
    1676             :  * Default implementation is GetDimensions().size(), and may be overridden by
    1677             :  * drivers if they have a faster / less expensive implementations.
    1678             :  *
    1679             :  * This is the same as the C function GDALMDArrayGetDimensionCount() or
    1680             :  * GDALAttributeGetDimensionCount().
    1681             :  *
    1682             :  */
    1683       27913 : size_t GDALAbstractMDArray::GetDimensionCount() const
    1684             : {
    1685       27913 :     return GetDimensions().size();
    1686             : }
    1687             : 
    1688             : /************************************************************************/
    1689             : /*                            Rename()                                  */
    1690             : /************************************************************************/
    1691             : 
    1692             : /** Rename the attribute/array.
    1693             :  *
    1694             :  * This is not implemented by all drivers.
    1695             :  *
    1696             :  * Drivers known to implement it: MEM, netCDF, Zarr.
    1697             :  *
    1698             :  * This is the same as the C functions GDALMDArrayRename() or
    1699             :  * GDALAttributeRename().
    1700             :  *
    1701             :  * @param osNewName New name.
    1702             :  *
    1703             :  * @return true in case of success
    1704             :  * @since GDAL 3.8
    1705             :  */
    1706           0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
    1707             : {
    1708           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
    1709           0 :     return false;
    1710             : }
    1711             : 
    1712             : /************************************************************************/
    1713             : /*                             CopyValue()                              */
    1714             : /************************************************************************/
    1715             : 
    1716             : /** Convert a value from a source type to a destination type.
    1717             :  *
    1718             :  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
    1719             :  * that must be freed with CPLFree().
    1720             :  */
    1721       85917 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
    1722             :                                      const GDALExtendedDataType &srcType,
    1723             :                                      void *pDst,
    1724             :                                      const GDALExtendedDataType &dstType)
    1725             : {
    1726      166499 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1727       80582 :         dstType.GetClass() == GEDTC_NUMERIC)
    1728             :     {
    1729       79568 :         GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
    1730             :                         dstType.GetNumericDataType(), 0, 1);
    1731       79568 :         return true;
    1732             :     }
    1733       11462 :     if (srcType.GetClass() == GEDTC_STRING &&
    1734        5113 :         dstType.GetClass() == GEDTC_STRING)
    1735             :     {
    1736             :         const char *srcStrPtr;
    1737        3865 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
    1738        3865 :         char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
    1739        3865 :         *reinterpret_cast<void **>(pDst) = pszDup;
    1740        3865 :         return true;
    1741             :     }
    1742        3498 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1743        1014 :         dstType.GetClass() == GEDTC_STRING)
    1744             :     {
    1745        1014 :         const char *str = nullptr;
    1746        1014 :         switch (srcType.GetNumericDataType())
    1747             :         {
    1748           0 :             case GDT_Unknown:
    1749           0 :                 break;
    1750           0 :             case GDT_UInt8:
    1751           0 :                 str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
    1752           0 :                 break;
    1753           3 :             case GDT_Int8:
    1754           3 :                 str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
    1755           3 :                 break;
    1756          72 :             case GDT_UInt16:
    1757          72 :                 str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
    1758          72 :                 break;
    1759           0 :             case GDT_Int16:
    1760           0 :                 str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
    1761           0 :                 break;
    1762          26 :             case GDT_UInt32:
    1763          26 :                 str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
    1764          26 :                 break;
    1765          69 :             case GDT_Int32:
    1766          69 :                 str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
    1767          69 :                 break;
    1768           0 :             case GDT_UInt64:
    1769             :                 str =
    1770           0 :                     CPLSPrintf(CPL_FRMT_GUIB,
    1771             :                                static_cast<GUIntBig>(
    1772             :                                    *static_cast<const std::uint64_t *>(pSrc)));
    1773           0 :                 break;
    1774          30 :             case GDT_Int64:
    1775          30 :                 str = CPLSPrintf(CPL_FRMT_GIB,
    1776             :                                  static_cast<GIntBig>(
    1777             :                                      *static_cast<const std::int64_t *>(pSrc)));
    1778          30 :                 break;
    1779           0 :             case GDT_Float16:
    1780           0 :                 str = CPLSPrintf("%.5g",
    1781             :                                  double(*static_cast<const GFloat16 *>(pSrc)));
    1782           0 :                 break;
    1783          60 :             case GDT_Float32:
    1784         120 :                 str = CPLSPrintf(
    1785             :                     "%.9g",
    1786          60 :                     static_cast<double>(*static_cast<const float *>(pSrc)));
    1787          60 :                 break;
    1788         752 :             case GDT_Float64:
    1789         752 :                 str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
    1790         752 :                 break;
    1791           2 :             case GDT_CInt16:
    1792             :             {
    1793           2 :                 const GInt16 *src = static_cast<const GInt16 *>(pSrc);
    1794           2 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
    1795           2 :                 break;
    1796             :             }
    1797           0 :             case GDT_CInt32:
    1798             :             {
    1799           0 :                 const GInt32 *src = static_cast<const GInt32 *>(pSrc);
    1800           0 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
    1801           0 :                 break;
    1802             :             }
    1803           0 :             case GDT_CFloat16:
    1804             :             {
    1805           0 :                 const GFloat16 *src = static_cast<const GFloat16 *>(pSrc);
    1806           0 :                 str = CPLSPrintf("%.5g+%.5gj", double(src[0]), double(src[1]));
    1807           0 :                 break;
    1808             :             }
    1809           0 :             case GDT_CFloat32:
    1810             :             {
    1811           0 :                 const float *src = static_cast<const float *>(pSrc);
    1812           0 :                 str = CPLSPrintf("%.9g+%.9gj", double(src[0]), double(src[1]));
    1813           0 :                 break;
    1814             :             }
    1815           0 :             case GDT_CFloat64:
    1816             :             {
    1817           0 :                 const double *src = static_cast<const double *>(pSrc);
    1818           0 :                 str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
    1819           0 :                 break;
    1820             :             }
    1821           0 :             case GDT_TypeCount:
    1822           0 :                 CPLAssert(false);
    1823             :                 break;
    1824             :         }
    1825        1014 :         char *pszDup = str ? CPLStrdup(str) : nullptr;
    1826        1014 :         *reinterpret_cast<void **>(pDst) = pszDup;
    1827        1014 :         return true;
    1828             :     }
    1829        2718 :     if (srcType.GetClass() == GEDTC_STRING &&
    1830        1248 :         dstType.GetClass() == GEDTC_NUMERIC)
    1831             :     {
    1832             :         const char *srcStrPtr;
    1833        1248 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
    1834        1248 :         if (dstType.GetNumericDataType() == GDT_Int64)
    1835             :         {
    1836           2 :             *(static_cast<int64_t *>(pDst)) =
    1837           2 :                 srcStrPtr == nullptr ? 0
    1838           1 :                                      : static_cast<int64_t>(atoll(srcStrPtr));
    1839             :         }
    1840        1246 :         else if (dstType.GetNumericDataType() == GDT_UInt64)
    1841             :         {
    1842           2 :             *(static_cast<uint64_t *>(pDst)) =
    1843           2 :                 srcStrPtr == nullptr
    1844           2 :                     ? 0
    1845           1 :                     : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
    1846             :         }
    1847             :         else
    1848             :         {
    1849        1244 :             const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
    1850        1244 :             GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
    1851             :                             dstType.GetNumericDataType(), 0, 1);
    1852             :         }
    1853        1248 :         return true;
    1854             :     }
    1855         444 :     if (srcType.GetClass() == GEDTC_COMPOUND &&
    1856         222 :         dstType.GetClass() == GEDTC_COMPOUND)
    1857             :     {
    1858         222 :         const auto &srcComponents = srcType.GetComponents();
    1859         222 :         const auto &dstComponents = dstType.GetComponents();
    1860         222 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
    1861         222 :         GByte *pabyDst = static_cast<GByte *>(pDst);
    1862             : 
    1863             :         std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
    1864         444 :             srcComponentMap;
    1865        1078 :         for (const auto &srcComp : srcComponents)
    1866             :         {
    1867         856 :             srcComponentMap[srcComp->GetName()] = &srcComp;
    1868             :         }
    1869         598 :         for (const auto &dstComp : dstComponents)
    1870             :         {
    1871         376 :             auto oIter = srcComponentMap.find(dstComp->GetName());
    1872         376 :             if (oIter == srcComponentMap.end())
    1873           0 :                 return false;
    1874         376 :             const auto &srcComp = *(oIter->second);
    1875        1128 :             if (!GDALExtendedDataType::CopyValue(
    1876         376 :                     pabySrc + srcComp->GetOffset(), srcComp->GetType(),
    1877         376 :                     pabyDst + dstComp->GetOffset(), dstComp->GetType()))
    1878             :             {
    1879           0 :                 return false;
    1880             :             }
    1881             :         }
    1882         222 :         return true;
    1883             :     }
    1884             : 
    1885           0 :     return false;
    1886             : }
    1887             : 
    1888             : /************************************************************************/
    1889             : /*                             CopyValues()                             */
    1890             : /************************************************************************/
    1891             : 
    1892             : /** Convert severals value from a source type to a destination type.
    1893             :  *
    1894             :  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
    1895             :  * that must be freed with CPLFree().
    1896             :  */
    1897         370 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
    1898             :                                       const GDALExtendedDataType &srcType,
    1899             :                                       GPtrDiff_t nSrcStrideInElts, void *pDst,
    1900             :                                       const GDALExtendedDataType &dstType,
    1901             :                                       GPtrDiff_t nDstStrideInElts,
    1902             :                                       size_t nValues)
    1903             : {
    1904             :     const auto nSrcStrideInBytes =
    1905         370 :         nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
    1906             :     const auto nDstStrideInBytes =
    1907         370 :         nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
    1908         636 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1909         266 :         dstType.GetClass() == GEDTC_NUMERIC &&
    1910         266 :         nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
    1911         266 :         nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
    1912         902 :         nDstStrideInBytes >= std::numeric_limits<int>::min() &&
    1913         266 :         nDstStrideInBytes <= std::numeric_limits<int>::max())
    1914             :     {
    1915         266 :         GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
    1916             :                         static_cast<int>(nSrcStrideInBytes), pDst,
    1917             :                         dstType.GetNumericDataType(),
    1918             :                         static_cast<int>(nDstStrideInBytes), nValues);
    1919             :     }
    1920             :     else
    1921             :     {
    1922         104 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
    1923         104 :         GByte *pabyDst = static_cast<GByte *>(pDst);
    1924         208 :         for (size_t i = 0; i < nValues; ++i)
    1925             :         {
    1926         104 :             if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
    1927           0 :                 return false;
    1928         104 :             pabySrc += nSrcStrideInBytes;
    1929         104 :             pabyDst += nDstStrideInBytes;
    1930             :         }
    1931             :     }
    1932         370 :     return true;
    1933             : }
    1934             : 
    1935             : /************************************************************************/
    1936             : /*                       CheckReadWriteParams()                         */
    1937             : /************************************************************************/
    1938             : //! @cond Doxygen_Suppress
    1939       10592 : bool GDALAbstractMDArray::CheckReadWriteParams(
    1940             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
    1941             :     const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
    1942             :     const void *buffer, const void *buffer_alloc_start,
    1943             :     size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
    1944             :     std::vector<GPtrDiff_t> &tmp_bufferStride) const
    1945             : {
    1946           0 :     const auto lamda_error = []()
    1947             :     {
    1948           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1949             :                  "Not all elements pointed by buffer will fit in "
    1950             :                  "[buffer_alloc_start, "
    1951             :                  "buffer_alloc_start + buffer_alloc_size]");
    1952           0 :     };
    1953             : 
    1954       10592 :     const auto &dims = GetDimensions();
    1955       10592 :     if (dims.empty())
    1956             :     {
    1957        4715 :         if (buffer_alloc_start)
    1958             :         {
    1959        4317 :             const size_t elementSize = bufferDataType.GetSize();
    1960        4317 :             const GByte *paby_buffer = static_cast<const GByte *>(buffer);
    1961        4317 :             const GByte *paby_buffer_alloc_start =
    1962             :                 static_cast<const GByte *>(buffer_alloc_start);
    1963        4317 :             const GByte *paby_buffer_alloc_end =
    1964             :                 paby_buffer_alloc_start + buffer_alloc_size;
    1965             : 
    1966        4317 :             if (paby_buffer < paby_buffer_alloc_start ||
    1967        4317 :                 paby_buffer + elementSize > paby_buffer_alloc_end)
    1968             :             {
    1969           0 :                 lamda_error();
    1970           0 :                 return false;
    1971             :             }
    1972             :         }
    1973        4715 :         return true;
    1974             :     }
    1975             : 
    1976        5877 :     if (arrayStep == nullptr)
    1977             :     {
    1978        1744 :         tmp_arrayStep.resize(dims.size(), 1);
    1979        1744 :         arrayStep = tmp_arrayStep.data();
    1980             :     }
    1981       16130 :     for (size_t i = 0; i < dims.size(); i++)
    1982             :     {
    1983       10253 :         assert(count);
    1984       10253 :         if (count[i] == 0)
    1985             :         {
    1986           0 :             CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
    1987             :                      static_cast<unsigned>(i));
    1988           0 :             return false;
    1989             :         }
    1990             :     }
    1991        5877 :     bool bufferStride_all_positive = true;
    1992        5877 :     if (bufferStride == nullptr)
    1993             :     {
    1994        1384 :         GPtrDiff_t stride = 1;
    1995        1384 :         assert(dims.empty() || count != nullptr);
    1996             :         // To compute strides we must proceed from the fastest varying dimension
    1997             :         // (the last one), and then reverse the result
    1998        3076 :         for (size_t i = dims.size(); i != 0;)
    1999             :         {
    2000        1692 :             --i;
    2001        1692 :             tmp_bufferStride.push_back(stride);
    2002        1692 :             GUInt64 newStride = 0;
    2003             :             bool bOK;
    2004             :             try
    2005             :             {
    2006        1692 :                 newStride = (CPLSM(static_cast<uint64_t>(stride)) *
    2007        3384 :                              CPLSM(static_cast<uint64_t>(count[i])))
    2008        1692 :                                 .v();
    2009        1692 :                 bOK = static_cast<size_t>(newStride) == newStride &&
    2010        1692 :                       newStride < std::numeric_limits<size_t>::max() / 2;
    2011             :             }
    2012           0 :             catch (...)
    2013             :             {
    2014           0 :                 bOK = false;
    2015             :             }
    2016        1692 :             if (!bOK)
    2017             :             {
    2018           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
    2019           0 :                 return false;
    2020             :             }
    2021        1692 :             stride = static_cast<GPtrDiff_t>(newStride);
    2022             :         }
    2023        1384 :         std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
    2024        1384 :         bufferStride = tmp_bufferStride.data();
    2025             :     }
    2026             :     else
    2027             :     {
    2028       13052 :         for (size_t i = 0; i < dims.size(); i++)
    2029             :         {
    2030        8560 :             if (bufferStride[i] < 0)
    2031             :             {
    2032           1 :                 bufferStride_all_positive = false;
    2033           1 :                 break;
    2034             :             }
    2035             :         }
    2036             :     }
    2037       16101 :     for (size_t i = 0; i < dims.size(); i++)
    2038             :     {
    2039       10234 :         assert(arrayStartIdx);
    2040       10234 :         assert(count);
    2041       10234 :         if (arrayStartIdx[i] >= dims[i]->GetSize())
    2042             :         {
    2043           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    2044             :                      "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
    2045             :                      static_cast<unsigned>(i),
    2046           2 :                      static_cast<GUInt64>(arrayStartIdx[i]),
    2047           2 :                      static_cast<GUInt64>(dims[i]->GetSize()));
    2048           2 :             return false;
    2049             :         }
    2050             :         bool bOverflow;
    2051       10232 :         if (arrayStep[i] >= 0)
    2052             :         {
    2053             :             try
    2054             :             {
    2055        9577 :                 bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
    2056        9579 :                              CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    2057       38311 :                                  CPLSM(static_cast<uint64_t>(arrayStep[i])))
    2058        9577 :                                 .v() >= dims[i]->GetSize();
    2059             :             }
    2060           1 :             catch (...)
    2061             :             {
    2062           1 :                 bOverflow = true;
    2063             :             }
    2064        9578 :             if (bOverflow)
    2065             :             {
    2066           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2067             :                          "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
    2068             :                          ">= " CPL_FRMT_GUIB,
    2069             :                          static_cast<unsigned>(i), static_cast<unsigned>(i),
    2070             :                          static_cast<unsigned>(i),
    2071           5 :                          static_cast<GUInt64>(dims[i]->GetSize()));
    2072           5 :                 return false;
    2073             :             }
    2074             :         }
    2075             :         else
    2076             :         {
    2077             :             try
    2078             :             {
    2079         654 :                 bOverflow =
    2080         654 :                     arrayStartIdx[i] <
    2081         654 :                     (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    2082        1308 :                      CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
    2083             :                                ? (static_cast<uint64_t>(1) << 63)
    2084        1308 :                                : static_cast<uint64_t>(-arrayStep[i])))
    2085         654 :                         .v();
    2086             :             }
    2087           0 :             catch (...)
    2088             :             {
    2089           0 :                 bOverflow = true;
    2090             :             }
    2091         654 :             if (bOverflow)
    2092             :             {
    2093           3 :                 CPLError(
    2094             :                     CE_Failure, CPLE_AppDefined,
    2095             :                     "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
    2096             :                     static_cast<unsigned>(i), static_cast<unsigned>(i),
    2097             :                     static_cast<unsigned>(i));
    2098           3 :                 return false;
    2099             :             }
    2100             :         }
    2101             :     }
    2102             : 
    2103        5867 :     if (buffer_alloc_start)
    2104             :     {
    2105        2810 :         const size_t elementSize = bufferDataType.GetSize();
    2106        2810 :         const GByte *paby_buffer = static_cast<const GByte *>(buffer);
    2107        2810 :         const GByte *paby_buffer_alloc_start =
    2108             :             static_cast<const GByte *>(buffer_alloc_start);
    2109        2810 :         const GByte *paby_buffer_alloc_end =
    2110             :             paby_buffer_alloc_start + buffer_alloc_size;
    2111        2810 :         if (bufferStride_all_positive)
    2112             :         {
    2113        2810 :             if (paby_buffer < paby_buffer_alloc_start)
    2114             :             {
    2115           0 :                 lamda_error();
    2116           0 :                 return false;
    2117             :             }
    2118        2810 :             GUInt64 nOffset = elementSize;
    2119        7996 :             for (size_t i = 0; i < dims.size(); i++)
    2120             :             {
    2121             :                 try
    2122             :                 {
    2123        5186 :                     nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
    2124        5186 :                                CPLSM(static_cast<uint64_t>(bufferStride[i])) *
    2125       10372 :                                    CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    2126       20744 :                                    CPLSM(static_cast<uint64_t>(elementSize)))
    2127        5186 :                                   .v();
    2128             :                 }
    2129           0 :                 catch (...)
    2130             :                 {
    2131           0 :                     lamda_error();
    2132           0 :                     return false;
    2133             :                 }
    2134             :             }
    2135             : #if SIZEOF_VOIDP == 4
    2136             :             if (static_cast<size_t>(nOffset) != nOffset)
    2137             :             {
    2138             :                 lamda_error();
    2139             :                 return false;
    2140             :             }
    2141             : #endif
    2142        2810 :             if (paby_buffer + nOffset > paby_buffer_alloc_end)
    2143             :             {
    2144           0 :                 lamda_error();
    2145           0 :                 return false;
    2146             :             }
    2147             :         }
    2148           0 :         else if (dims.size() < 31)
    2149             :         {
    2150             :             // Check all corners of the hypercube
    2151           0 :             const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
    2152           0 :             for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
    2153             :             {
    2154           0 :                 const GByte *paby = paby_buffer;
    2155           0 :                 for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
    2156             :                      i++)
    2157             :                 {
    2158           0 :                     if (iCornerCode & (1U << i))
    2159             :                     {
    2160             :                         // We should check for integer overflows
    2161           0 :                         paby += bufferStride[i] * (count[i] - 1) * elementSize;
    2162             :                     }
    2163             :                 }
    2164           0 :                 if (paby < paby_buffer_alloc_start ||
    2165           0 :                     paby + elementSize > paby_buffer_alloc_end)
    2166             :                 {
    2167           0 :                     lamda_error();
    2168           0 :                     return false;
    2169             :                 }
    2170             :             }
    2171             :         }
    2172             :     }
    2173             : 
    2174        5867 :     return true;
    2175             : }
    2176             : 
    2177             : //! @endcond
    2178             : 
    2179             : /************************************************************************/
    2180             : /*                               Read()                                 */
    2181             : /************************************************************************/
    2182             : 
    2183             : /** Read part or totality of a multidimensional array or attribute.
    2184             :  *
    2185             :  * This will extract the content of a hyper-rectangle from the array into
    2186             :  * a user supplied buffer.
    2187             :  *
    2188             :  * If bufferDataType is of type string, the values written in pDstBuffer
    2189             :  * will be char* pointers and the strings should be freed with CPLFree().
    2190             :  *
    2191             :  * This is the same as the C function GDALMDArrayRead().
    2192             :  *
    2193             :  * @param arrayStartIdx Values representing the starting index to read
    2194             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2195             :  *                      Array of GetDimensionCount() values. Must not be
    2196             :  *                      nullptr, unless for a zero-dimensional array.
    2197             :  *
    2198             :  * @param count         Values representing the number of values to extract in
    2199             :  *                      each dimension.
    2200             :  *                      Array of GetDimensionCount() values. Must not be
    2201             :  *                      nullptr, unless for a zero-dimensional array.
    2202             :  *
    2203             :  * @param arrayStep     Spacing between values to extract in each dimension.
    2204             :  *                      The spacing is in number of array elements, not bytes.
    2205             :  *                      If provided, must contain GetDimensionCount() values.
    2206             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
    2207             :  * default to indicate consecutive elements.
    2208             :  *
    2209             :  * @param bufferStride  Spacing between values to store in pDstBuffer.
    2210             :  *                      The spacing is in number of array elements, not bytes.
    2211             :  *                      If provided, must contain GetDimensionCount() values.
    2212             :  *                      Negative values are possible (for example to reorder
    2213             :  *                      from bottom-to-top to top-to-bottom).
    2214             :  *                      If set to nullptr, will be set so that pDstBuffer is
    2215             :  *                      written in a compact way, with elements of the last /
    2216             :  *                      fastest varying dimension being consecutive.
    2217             :  *
    2218             :  * @param bufferDataType Data type of values in pDstBuffer.
    2219             :  *
    2220             :  * @param pDstBuffer    User buffer to store the values read. Should be big
    2221             :  *                      enough to store the number of values indicated by
    2222             :  * count[] and with the spacing of bufferStride[].
    2223             :  *
    2224             :  * @param pDstBufferAllocStart Optional pointer that can be used to validate the
    2225             :  *                             validity of pDstBuffer. pDstBufferAllocStart
    2226             :  * should be the pointer returned by the malloc() or equivalent call used to
    2227             :  * allocate the buffer. It will generally be equal to pDstBuffer (when
    2228             :  * bufferStride[] values are all positive), but not necessarily. If specified,
    2229             :  * nDstBufferAllocSize should be also set to the appropriate value. If no
    2230             :  * validation is needed, nullptr can be passed.
    2231             :  *
    2232             :  * @param nDstBufferAllocSize  Optional buffer size, that can be used to
    2233             :  * validate the validity of pDstBuffer. This is the size of the buffer starting
    2234             :  * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
    2235             :  *                             set to the appropriate value.
    2236             :  *                             If no validation is needed, 0 can be passed.
    2237             :  *
    2238             :  * @return true in case of success.
    2239             :  */
    2240        3930 : bool GDALAbstractMDArray::Read(
    2241             :     const GUInt64 *arrayStartIdx, const size_t *count,
    2242             :     const GInt64 *arrayStep,         // step in elements
    2243             :     const GPtrDiff_t *bufferStride,  // stride in elements
    2244             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    2245             :     const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
    2246             : {
    2247        3930 :     if (!GetDataType().CanConvertTo(bufferDataType))
    2248             :     {
    2249           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2250             :                  "Array data type is not convertible to buffer data type");
    2251           0 :         return false;
    2252             :     }
    2253             : 
    2254        7860 :     std::vector<GInt64> tmp_arrayStep;
    2255        7860 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    2256        3930 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    2257             :                               bufferDataType, pDstBuffer, pDstBufferAllocStart,
    2258             :                               nDstBufferAllocSize, tmp_arrayStep,
    2259             :                               tmp_bufferStride))
    2260             :     {
    2261           0 :         return false;
    2262             :     }
    2263             : 
    2264        3930 :     return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
    2265        3930 :                  pDstBuffer);
    2266             : }
    2267             : 
    2268             : /************************************************************************/
    2269             : /*                                IWrite()                              */
    2270             : /************************************************************************/
    2271             : 
    2272             : //! @cond Doxygen_Suppress
    2273           1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
    2274             :                                  const GInt64 *, const GPtrDiff_t *,
    2275             :                                  const GDALExtendedDataType &, const void *)
    2276             : {
    2277           1 :     CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
    2278           1 :     return false;
    2279             : }
    2280             : 
    2281             : //! @endcond
    2282             : 
    2283             : /************************************************************************/
    2284             : /*                               Write()                                 */
    2285             : /************************************************************************/
    2286             : 
    2287             : /** Write part or totality of a multidimensional array or attribute.
    2288             :  *
    2289             :  * This will set the content of a hyper-rectangle into the array from
    2290             :  * a user supplied buffer.
    2291             :  *
    2292             :  * If bufferDataType is of type string, the values read from pSrcBuffer
    2293             :  * will be char* pointers.
    2294             :  *
    2295             :  * This is the same as the C function GDALMDArrayWrite().
    2296             :  *
    2297             :  * @param arrayStartIdx Values representing the starting index to write
    2298             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2299             :  *                      Array of GetDimensionCount() values. Must not be
    2300             :  *                      nullptr, unless for a zero-dimensional array.
    2301             :  *
    2302             :  * @param count         Values representing the number of values to write in
    2303             :  *                      each dimension.
    2304             :  *                      Array of GetDimensionCount() values. Must not be
    2305             :  *                      nullptr, unless for a zero-dimensional array.
    2306             :  *
    2307             :  * @param arrayStep     Spacing between values to write in each dimension.
    2308             :  *                      The spacing is in number of array elements, not bytes.
    2309             :  *                      If provided, must contain GetDimensionCount() values.
    2310             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
    2311             :  * default to indicate consecutive elements.
    2312             :  *
    2313             :  * @param bufferStride  Spacing between values to read from pSrcBuffer.
    2314             :  *                      The spacing is in number of array elements, not bytes.
    2315             :  *                      If provided, must contain GetDimensionCount() values.
    2316             :  *                      Negative values are possible (for example to reorder
    2317             :  *                      from bottom-to-top to top-to-bottom).
    2318             :  *                      If set to nullptr, will be set so that pSrcBuffer is
    2319             :  *                      written in a compact way, with elements of the last /
    2320             :  *                      fastest varying dimension being consecutive.
    2321             :  *
    2322             :  * @param bufferDataType Data type of values in pSrcBuffer.
    2323             :  *
    2324             :  * @param pSrcBuffer    User buffer to read the values from. Should be big
    2325             :  *                      enough to store the number of values indicated by
    2326             :  * count[] and with the spacing of bufferStride[].
    2327             :  *
    2328             :  * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
    2329             :  *                             validity of pSrcBuffer. pSrcBufferAllocStart
    2330             :  * should be the pointer returned by the malloc() or equivalent call used to
    2331             :  * allocate the buffer. It will generally be equal to pSrcBuffer (when
    2332             :  * bufferStride[] values are all positive), but not necessarily. If specified,
    2333             :  * nSrcBufferAllocSize should be also set to the appropriate value. If no
    2334             :  * validation is needed, nullptr can be passed.
    2335             :  *
    2336             :  * @param nSrcBufferAllocSize  Optional buffer size, that can be used to
    2337             :  * validate the validity of pSrcBuffer. This is the size of the buffer starting
    2338             :  * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
    2339             :  *                             set to the appropriate value.
    2340             :  *                             If no validation is needed, 0 can be passed.
    2341             :  *
    2342             :  * @return true in case of success.
    2343             :  */
    2344        2185 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
    2345             :                                 const size_t *count, const GInt64 *arrayStep,
    2346             :                                 const GPtrDiff_t *bufferStride,
    2347             :                                 const GDALExtendedDataType &bufferDataType,
    2348             :                                 const void *pSrcBuffer,
    2349             :                                 const void *pSrcBufferAllocStart,
    2350             :                                 size_t nSrcBufferAllocSize)
    2351             : {
    2352        2185 :     if (!bufferDataType.CanConvertTo(GetDataType()))
    2353             :     {
    2354           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2355             :                  "Buffer data type is not convertible to array data type");
    2356           0 :         return false;
    2357             :     }
    2358             : 
    2359        4370 :     std::vector<GInt64> tmp_arrayStep;
    2360        4370 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    2361        2185 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    2362             :                               bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
    2363             :                               nSrcBufferAllocSize, tmp_arrayStep,
    2364             :                               tmp_bufferStride))
    2365             :     {
    2366           0 :         return false;
    2367             :     }
    2368             : 
    2369        2185 :     return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
    2370        2185 :                   pSrcBuffer);
    2371             : }
    2372             : 
    2373             : /************************************************************************/
    2374             : /*                          GetTotalElementsCount()                     */
    2375             : /************************************************************************/
    2376             : 
    2377             : /** Return the total number of values in the array.
    2378             :  *
    2379             :  * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
    2380             :  * and GDALAttributeGetTotalElementsCount().
    2381             :  *
    2382             :  */
    2383        1462 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
    2384             : {
    2385        1462 :     const auto &dims = GetDimensions();
    2386        1462 :     if (dims.empty())
    2387         792 :         return 1;
    2388         670 :     GUInt64 nElts = 1;
    2389        1478 :     for (const auto &dim : dims)
    2390             :     {
    2391             :         try
    2392             :         {
    2393         808 :             nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
    2394        2424 :                      CPLSM(static_cast<uint64_t>(dim->GetSize())))
    2395         808 :                         .v();
    2396             :         }
    2397           0 :         catch (...)
    2398             :         {
    2399           0 :             return 0;
    2400             :         }
    2401             :     }
    2402         670 :     return nElts;
    2403             : }
    2404             : 
    2405             : /************************************************************************/
    2406             : /*                           GetBlockSize()                             */
    2407             : /************************************************************************/
    2408             : 
    2409             : /** Return the "natural" block size of the array along all dimensions.
    2410             :  *
    2411             :  * Some drivers might organize the array in tiles/blocks and reading/writing
    2412             :  * aligned on those tile/block boundaries will be more efficient.
    2413             :  *
    2414             :  * The returned number of elements in the vector is the same as
    2415             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
    2416             :  * the natural block size along the considered dimension.
    2417             :  * "Flat" arrays will typically return a vector of values set to 0.
    2418             :  *
    2419             :  * The default implementation will return a vector of values set to 0.
    2420             :  *
    2421             :  * This method is used by GetProcessingChunkSize().
    2422             :  *
    2423             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikely
    2424             :  * theoretical case of a 32-bit platform, this might exceed its size_t
    2425             :  * allocation capabilities.
    2426             :  *
    2427             :  * This is the same as the C function GDALMDArrayGetBlockSize().
    2428             :  *
    2429             :  * @return the block size, in number of elements along each dimension.
    2430             :  */
    2431         297 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
    2432             : {
    2433         297 :     return std::vector<GUInt64>(GetDimensionCount());
    2434             : }
    2435             : 
    2436             : /************************************************************************/
    2437             : /*                       GetProcessingChunkSize()                       */
    2438             : /************************************************************************/
    2439             : 
    2440             : /** \brief Return an optimal chunk size for read/write operations, given the
    2441             :  * natural block size and memory constraints specified.
    2442             :  *
    2443             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
    2444             :  * multiple of those returned by GetBlockSize() (unless the block define by
    2445             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
    2446             :  * returned by this method).
    2447             :  *
    2448             :  * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
    2449             :  *
    2450             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
    2451             :  * chunk.
    2452             :  *
    2453             :  * @return the chunk size, in number of elements along each dimension.
    2454             :  */
    2455             : std::vector<size_t>
    2456          90 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
    2457             : {
    2458          90 :     const auto &dims = GetDimensions();
    2459          90 :     const auto &nDTSize = GetDataType().GetSize();
    2460          90 :     std::vector<size_t> anChunkSize;
    2461         180 :     auto blockSize = GetBlockSize();
    2462          90 :     CPLAssert(blockSize.size() == dims.size());
    2463          90 :     size_t nChunkSize = nDTSize;
    2464          90 :     bool bOverflow = false;
    2465          90 :     constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
    2466             :     // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
    2467             :     // [1, min(sizet_max, dim_size[i])]
    2468             :     // Also make sure that the product of all anChunkSize[i]) fits on size_t
    2469         246 :     for (size_t i = 0; i < dims.size(); i++)
    2470             :     {
    2471             :         const auto sizeDimI =
    2472         312 :             std::max(static_cast<size_t>(1),
    2473         312 :                      static_cast<size_t>(
    2474         312 :                          std::min(static_cast<GUInt64>(kSIZE_T_MAX),
    2475         156 :                                   std::min(blockSize[i], dims[i]->GetSize()))));
    2476         156 :         anChunkSize.push_back(sizeDimI);
    2477         156 :         if (nChunkSize > kSIZE_T_MAX / sizeDimI)
    2478             :         {
    2479           4 :             bOverflow = true;
    2480             :         }
    2481             :         else
    2482             :         {
    2483         152 :             nChunkSize *= sizeDimI;
    2484             :         }
    2485             :     }
    2486          90 :     if (nChunkSize == 0)
    2487           0 :         return anChunkSize;
    2488             : 
    2489             :     // If the product of all anChunkSize[i] does not fit on size_t, then
    2490             :     // set lowest anChunkSize[i] to 1.
    2491          90 :     if (bOverflow)
    2492             :     {
    2493           2 :         nChunkSize = nDTSize;
    2494           2 :         bOverflow = false;
    2495           8 :         for (size_t i = dims.size(); i > 0;)
    2496             :         {
    2497           6 :             --i;
    2498           6 :             if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
    2499             :             {
    2500           4 :                 bOverflow = true;
    2501           4 :                 anChunkSize[i] = 1;
    2502             :             }
    2503             :             else
    2504             :             {
    2505           2 :                 nChunkSize *= anChunkSize[i];
    2506             :             }
    2507             :         }
    2508             :     }
    2509             : 
    2510          90 :     nChunkSize = nDTSize;
    2511         180 :     std::vector<size_t> anAccBlockSizeFromStart;
    2512         246 :     for (size_t i = 0; i < dims.size(); i++)
    2513             :     {
    2514         156 :         nChunkSize *= anChunkSize[i];
    2515         156 :         anAccBlockSizeFromStart.push_back(nChunkSize);
    2516             :     }
    2517          90 :     if (nChunkSize <= nMaxChunkMemory / 2)
    2518             :     {
    2519          86 :         size_t nVoxelsFromEnd = 1;
    2520         234 :         for (size_t i = dims.size(); i > 0;)
    2521             :         {
    2522         148 :             --i;
    2523             :             const auto nCurBlockSize =
    2524         148 :                 anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
    2525         148 :             const auto nMul = nMaxChunkMemory / nCurBlockSize;
    2526         148 :             if (nMul >= 2)
    2527             :             {
    2528         140 :                 const auto nSizeThisDim(dims[i]->GetSize());
    2529             :                 const auto nBlocksThisDim =
    2530         140 :                     DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
    2531         140 :                 anChunkSize[i] = static_cast<size_t>(std::min(
    2532         140 :                     anChunkSize[i] *
    2533         280 :                         std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
    2534         140 :                     nSizeThisDim));
    2535             :             }
    2536         148 :             nVoxelsFromEnd *= anChunkSize[i];
    2537             :         }
    2538             :     }
    2539          90 :     return anChunkSize;
    2540             : }
    2541             : 
    2542             : /************************************************************************/
    2543             : /*                         BaseRename()                                 */
    2544             : /************************************************************************/
    2545             : 
    2546             : //! @cond Doxygen_Suppress
    2547          18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
    2548             : {
    2549          18 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
    2550          18 :     m_osFullName += osNewName;
    2551          18 :     m_osName = osNewName;
    2552             : 
    2553          18 :     NotifyChildrenOfRenaming();
    2554          18 : }
    2555             : 
    2556             : //! @endcond
    2557             : 
    2558             : //! @cond Doxygen_Suppress
    2559             : /************************************************************************/
    2560             : /*                          ParentRenamed()                             */
    2561             : /************************************************************************/
    2562             : 
    2563          50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
    2564             : {
    2565          50 :     m_osFullName = osNewParentFullName;
    2566          50 :     m_osFullName += "/";
    2567          50 :     m_osFullName += m_osName;
    2568             : 
    2569          50 :     NotifyChildrenOfRenaming();
    2570          50 : }
    2571             : 
    2572             : //! @endcond
    2573             : 
    2574             : /************************************************************************/
    2575             : /*                             Deleted()                                */
    2576             : /************************************************************************/
    2577             : 
    2578             : //! @cond Doxygen_Suppress
    2579          52 : void GDALAbstractMDArray::Deleted()
    2580             : {
    2581          52 :     m_bValid = false;
    2582             : 
    2583          52 :     NotifyChildrenOfDeletion();
    2584          52 : }
    2585             : 
    2586             : //! @endcond
    2587             : 
    2588             : /************************************************************************/
    2589             : /*                        ParentDeleted()                               */
    2590             : /************************************************************************/
    2591             : 
    2592             : //! @cond Doxygen_Suppress
    2593          28 : void GDALAbstractMDArray::ParentDeleted()
    2594             : {
    2595          28 :     Deleted();
    2596          28 : }
    2597             : 
    2598             : //! @endcond
    2599             : 
    2600             : /************************************************************************/
    2601             : /*                     CheckValidAndErrorOutIfNot()                     */
    2602             : /************************************************************************/
    2603             : 
    2604             : //! @cond Doxygen_Suppress
    2605        6793 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
    2606             : {
    2607        6793 :     if (!m_bValid)
    2608             :     {
    2609          26 :         CPLError(CE_Failure, CPLE_AppDefined,
    2610             :                  "This object has been deleted. No action on it is possible");
    2611             :     }
    2612        6793 :     return m_bValid;
    2613             : }
    2614             : 
    2615             : //! @endcond
    2616             : 
    2617             : /************************************************************************/
    2618             : /*                             SetUnit()                                */
    2619             : /************************************************************************/
    2620             : 
    2621             : /** Set the variable unit.
    2622             :  *
    2623             :  * Values should conform as much as possible with those allowed by
    2624             :  * the NetCDF CF conventions:
    2625             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
    2626             :  * but others might be returned.
    2627             :  *
    2628             :  * Few examples are "meter", "degrees", "second", ...
    2629             :  * Empty value means unknown.
    2630             :  *
    2631             :  * This is the same as the C function GDALMDArraySetUnit()
    2632             :  *
    2633             :  * @note Driver implementation: optionally implemented.
    2634             :  *
    2635             :  * @param osUnit unit name.
    2636             :  * @return true in case of success.
    2637             :  */
    2638           0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
    2639             : {
    2640           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
    2641           0 :     return false;
    2642             : }
    2643             : 
    2644             : /************************************************************************/
    2645             : /*                             GetUnit()                                */
    2646             : /************************************************************************/
    2647             : 
    2648             : /** Return the array unit.
    2649             :  *
    2650             :  * Values should conform as much as possible with those allowed by
    2651             :  * the NetCDF CF conventions:
    2652             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
    2653             :  * but others might be returned.
    2654             :  *
    2655             :  * Few examples are "meter", "degrees", "second", ...
    2656             :  * Empty value means unknown.
    2657             :  *
    2658             :  * This is the same as the C function GDALMDArrayGetUnit()
    2659             :  */
    2660           5 : const std::string &GDALMDArray::GetUnit() const
    2661             : {
    2662           5 :     static const std::string emptyString;
    2663           5 :     return emptyString;
    2664             : }
    2665             : 
    2666             : /************************************************************************/
    2667             : /*                          SetSpatialRef()                             */
    2668             : /************************************************************************/
    2669             : 
    2670             : /** Assign a spatial reference system object to the array.
    2671             :  *
    2672             :  * This is the same as the C function GDALMDArraySetSpatialRef().
    2673             :  */
    2674           0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
    2675             : {
    2676           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
    2677           0 :     return false;
    2678             : }
    2679             : 
    2680             : /************************************************************************/
    2681             : /*                          GetSpatialRef()                             */
    2682             : /************************************************************************/
    2683             : 
    2684             : /** Return the spatial reference system object associated with the array.
    2685             :  *
    2686             :  * This is the same as the C function GDALMDArrayGetSpatialRef().
    2687             :  */
    2688           4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
    2689             : {
    2690           4 :     return nullptr;
    2691             : }
    2692             : 
    2693             : /************************************************************************/
    2694             : /*                        GetRawNoDataValue()                           */
    2695             : /************************************************************************/
    2696             : 
    2697             : /** Return the nodata value as a "raw" value.
    2698             :  *
    2699             :  * The value returned might be nullptr in case of no nodata value. When
    2700             :  * a nodata value is registered, a non-nullptr will be returned whose size in
    2701             :  * bytes is GetDataType().GetSize().
    2702             :  *
    2703             :  * The returned value should not be modified or freed. It is valid until
    2704             :  * the array is destroyed, or the next call to GetRawNoDataValue() or
    2705             :  * SetRawNoDataValue(), or any similar methods.
    2706             :  *
    2707             :  * @note Driver implementation: this method shall be implemented if nodata
    2708             :  * is supported.
    2709             :  *
    2710             :  * This is the same as the C function GDALMDArrayGetRawNoDataValue().
    2711             :  *
    2712             :  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
    2713             :  */
    2714           5 : const void *GDALMDArray::GetRawNoDataValue() const
    2715             : {
    2716           5 :     return nullptr;
    2717             : }
    2718             : 
    2719             : /************************************************************************/
    2720             : /*                        GetNoDataValueAsDouble()                      */
    2721             : /************************************************************************/
    2722             : 
    2723             : /** Return the nodata value as a double.
    2724             :  *
    2725             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
    2726             :  *
    2727             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2728             :  * a nodata value exists and can be converted to double. Might be nullptr.
    2729             :  *
    2730             :  * @return the nodata value as a double. A 0.0 value might also indicate the
    2731             :  * absence of a nodata value or an error in the conversion (*pbHasNoData will be
    2732             :  * set to false then).
    2733             :  */
    2734       22515 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
    2735             : {
    2736       22515 :     const void *pNoData = GetRawNoDataValue();
    2737       22515 :     double dfNoData = 0.0;
    2738       22515 :     const auto &eDT = GetDataType();
    2739       22515 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2740       22515 :     if (ok)
    2741             :     {
    2742       22194 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
    2743             :                         GDT_Float64, 0, 1);
    2744             :     }
    2745       22515 :     if (pbHasNoData)
    2746         474 :         *pbHasNoData = ok;
    2747       22515 :     return dfNoData;
    2748             : }
    2749             : 
    2750             : /************************************************************************/
    2751             : /*                        GetNoDataValueAsInt64()                       */
    2752             : /************************************************************************/
    2753             : 
    2754             : /** Return the nodata value as a Int64.
    2755             :  *
    2756             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2757             :  * a nodata value exists and can be converted to Int64. Might be nullptr.
    2758             :  *
    2759             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
    2760             :  *
    2761             :  * @return the nodata value as a Int64
    2762             :  *
    2763             :  * @since GDAL 3.5
    2764             :  */
    2765          12 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
    2766             : {
    2767          12 :     const void *pNoData = GetRawNoDataValue();
    2768          12 :     int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
    2769          12 :     const auto &eDT = GetDataType();
    2770          12 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2771          12 :     if (ok)
    2772             :     {
    2773           8 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
    2774             :                         GDT_Int64, 0, 1);
    2775             :     }
    2776          12 :     if (pbHasNoData)
    2777          12 :         *pbHasNoData = ok;
    2778          12 :     return nNoData;
    2779             : }
    2780             : 
    2781             : /************************************************************************/
    2782             : /*                       GetNoDataValueAsUInt64()                       */
    2783             : /************************************************************************/
    2784             : 
    2785             : /** Return the nodata value as a UInt64.
    2786             :  *
    2787             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
    2788             : 
    2789             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2790             :  * a nodata value exists and can be converted to UInt64. Might be nullptr.
    2791             :  *
    2792             :  * @return the nodata value as a UInt64
    2793             :  *
    2794             :  * @since GDAL 3.5
    2795             :  */
    2796           8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
    2797             : {
    2798           8 :     const void *pNoData = GetRawNoDataValue();
    2799           8 :     uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
    2800           8 :     const auto &eDT = GetDataType();
    2801           8 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2802           8 :     if (ok)
    2803             :     {
    2804           6 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
    2805             :                         GDT_UInt64, 0, 1);
    2806             :     }
    2807           8 :     if (pbHasNoData)
    2808           8 :         *pbHasNoData = ok;
    2809           8 :     return nNoData;
    2810             : }
    2811             : 
    2812             : /************************************************************************/
    2813             : /*                        SetRawNoDataValue()                           */
    2814             : /************************************************************************/
    2815             : 
    2816             : /** Set the nodata value as a "raw" value.
    2817             :  *
    2818             :  * The value passed might be nullptr in case of no nodata value. When
    2819             :  * a nodata value is registered, a non-nullptr whose size in
    2820             :  * bytes is GetDataType().GetSize() must be passed.
    2821             :  *
    2822             :  * This is the same as the C function GDALMDArraySetRawNoDataValue().
    2823             :  *
    2824             :  * @note Driver implementation: this method shall be implemented if setting
    2825             :  nodata
    2826             :  * is supported.
    2827             : 
    2828             :  * @return true in case of success.
    2829             :  */
    2830           0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
    2831             : {
    2832           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2833             :              "SetRawNoDataValue() not implemented");
    2834           0 :     return false;
    2835             : }
    2836             : 
    2837             : /************************************************************************/
    2838             : /*                           SetNoDataValue()                           */
    2839             : /************************************************************************/
    2840             : 
    2841             : /** Set the nodata value as a double.
    2842             :  *
    2843             :  * If the natural data type of the attribute/array is not double, type
    2844             :  * conversion will occur to the type returned by GetDataType().
    2845             :  *
    2846             :  * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
    2847             :  *
    2848             :  * @return true in case of success.
    2849             :  */
    2850          61 : bool GDALMDArray::SetNoDataValue(double dfNoData)
    2851             : {
    2852          61 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2853          61 :     bool bRet = false;
    2854          61 :     if (GDALExtendedDataType::CopyValue(
    2855         122 :             &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
    2856          61 :             GetDataType()))
    2857             :     {
    2858          61 :         bRet = SetRawNoDataValue(pRawNoData);
    2859             :     }
    2860          61 :     CPLFree(pRawNoData);
    2861          61 :     return bRet;
    2862             : }
    2863             : 
    2864             : /************************************************************************/
    2865             : /*                           SetNoDataValue()                           */
    2866             : /************************************************************************/
    2867             : 
    2868             : /** Set the nodata value as a Int64.
    2869             :  *
    2870             :  * If the natural data type of the attribute/array is not Int64, type conversion
    2871             :  * will occur to the type returned by GetDataType().
    2872             :  *
    2873             :  * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
    2874             :  *
    2875             :  * @return true in case of success.
    2876             :  *
    2877             :  * @since GDAL 3.5
    2878             :  */
    2879           3 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
    2880             : {
    2881           3 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2882           3 :     bool bRet = false;
    2883           3 :     if (GDALExtendedDataType::CopyValue(&nNoData,
    2884           6 :                                         GDALExtendedDataType::Create(GDT_Int64),
    2885           3 :                                         pRawNoData, GetDataType()))
    2886             :     {
    2887           3 :         bRet = SetRawNoDataValue(pRawNoData);
    2888             :     }
    2889           3 :     CPLFree(pRawNoData);
    2890           3 :     return bRet;
    2891             : }
    2892             : 
    2893             : /************************************************************************/
    2894             : /*                           SetNoDataValue()                           */
    2895             : /************************************************************************/
    2896             : 
    2897             : /** Set the nodata value as a Int64.
    2898             :  *
    2899             :  * If the natural data type of the attribute/array is not Int64, type conversion
    2900             :  * will occur to the type returned by GetDataType().
    2901             :  *
    2902             :  * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
    2903             :  *
    2904             :  * @return true in case of success.
    2905             :  *
    2906             :  * @since GDAL 3.5
    2907             :  */
    2908           1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
    2909             : {
    2910           1 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2911           1 :     bool bRet = false;
    2912           1 :     if (GDALExtendedDataType::CopyValue(
    2913           2 :             &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
    2914           1 :             GetDataType()))
    2915             :     {
    2916           1 :         bRet = SetRawNoDataValue(pRawNoData);
    2917             :     }
    2918           1 :     CPLFree(pRawNoData);
    2919           1 :     return bRet;
    2920             : }
    2921             : 
    2922             : /************************************************************************/
    2923             : /*                            Resize()                                  */
    2924             : /************************************************************************/
    2925             : 
    2926             : /** Resize an array to new dimensions.
    2927             :  *
    2928             :  * Not all drivers may allow this operation, and with restrictions (e.g.
    2929             :  * for netCDF, this is limited to growing of "unlimited" dimensions)
    2930             :  *
    2931             :  * Resizing a dimension used in other arrays will cause those other arrays
    2932             :  * to be resized.
    2933             :  *
    2934             :  * This is the same as the C function GDALMDArrayResize().
    2935             :  *
    2936             :  * @param anNewDimSizes Array of GetDimensionCount() values containing the
    2937             :  *                      new size of each indexing dimension.
    2938             :  * @param papszOptions Options. (Driver specific)
    2939             :  * @return true in case of success.
    2940             :  * @since GDAL 3.7
    2941             :  */
    2942           0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
    2943             :                          CPL_UNUSED CSLConstList papszOptions)
    2944             : {
    2945           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2946             :              "Resize() is not supported for this array");
    2947           0 :     return false;
    2948             : }
    2949             : 
    2950             : /************************************************************************/
    2951             : /*                               SetScale()                             */
    2952             : /************************************************************************/
    2953             : 
    2954             : /** Set the scale value to apply to raw values.
    2955             :  *
    2956             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2957             :  *
    2958             :  * This is the same as the C function GDALMDArraySetScale() /
    2959             :  * GDALMDArraySetScaleEx().
    2960             :  *
    2961             :  * @note Driver implementation: this method shall be implemented if setting
    2962             :  * scale is supported.
    2963             :  *
    2964             :  * @param dfScale scale
    2965             :  * @param eStorageType Data type to which create the potential attribute that
    2966             :  * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
    2967             :  * implementation will decide automatically the data type. Note that changing
    2968             :  * the data type after initial setting might not be supported.
    2969             :  * @return true in case of success.
    2970             :  */
    2971           0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
    2972             :                            CPL_UNUSED GDALDataType eStorageType)
    2973             : {
    2974           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
    2975           0 :     return false;
    2976             : }
    2977             : 
    2978             : /************************************************************************/
    2979             : /*                               SetOffset)                             */
    2980             : /************************************************************************/
    2981             : 
    2982             : /** Set the offset value to apply to raw values.
    2983             :  *
    2984             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2985             :  *
    2986             :  * This is the same as the C function GDALMDArraySetOffset() /
    2987             :  * GDALMDArraySetOffsetEx().
    2988             :  *
    2989             :  * @note Driver implementation: this method shall be implemented if setting
    2990             :  * offset is supported.
    2991             :  *
    2992             :  * @param dfOffset Offset
    2993             :  * @param eStorageType Data type to which create the potential attribute that
    2994             :  * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
    2995             :  * implementation will decide automatically the data type. Note that changing
    2996             :  * the data type after initial setting might not be supported.
    2997             :  * @return true in case of success.
    2998             :  */
    2999           0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
    3000             :                             CPL_UNUSED GDALDataType eStorageType)
    3001             : {
    3002           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
    3003           0 :     return false;
    3004             : }
    3005             : 
    3006             : /************************************************************************/
    3007             : /*                               GetScale()                             */
    3008             : /************************************************************************/
    3009             : 
    3010             : /** Get the scale value to apply to raw values.
    3011             :  *
    3012             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    3013             :  *
    3014             :  * This is the same as the C function GDALMDArrayGetScale().
    3015             :  *
    3016             :  * @note Driver implementation: this method shall be implemented if getting
    3017             :  * scale is supported.
    3018             :  *
    3019             :  * @param pbHasScale Pointer to a output boolean that will be set to true if
    3020             :  * a scale value exists. Might be nullptr.
    3021             :  * @param peStorageType Pointer to a output GDALDataType that will be set to
    3022             :  * the storage type of the scale value, when known/relevant. Otherwise will be
    3023             :  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
    3024             :  *
    3025             :  * @return the scale value. A 1.0 value might also indicate the
    3026             :  * absence of a scale value.
    3027             :  */
    3028          20 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
    3029             :                              CPL_UNUSED GDALDataType *peStorageType) const
    3030             : {
    3031          20 :     if (pbHasScale)
    3032          20 :         *pbHasScale = false;
    3033          20 :     return 1.0;
    3034             : }
    3035             : 
    3036             : /************************************************************************/
    3037             : /*                               GetOffset()                            */
    3038             : /************************************************************************/
    3039             : 
    3040             : /** Get the offset value to apply to raw values.
    3041             :  *
    3042             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    3043             :  *
    3044             :  * This is the same as the C function GDALMDArrayGetOffset().
    3045             :  *
    3046             :  * @note Driver implementation: this method shall be implemented if getting
    3047             :  * offset is supported.
    3048             :  *
    3049             :  * @param pbHasOffset Pointer to a output boolean that will be set to true if
    3050             :  * a offset value exists. Might be nullptr.
    3051             :  * @param peStorageType Pointer to a output GDALDataType that will be set to
    3052             :  * the storage type of the offset value, when known/relevant. Otherwise will be
    3053             :  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
    3054             :  *
    3055             :  * @return the offset value. A 0.0 value might also indicate the
    3056             :  * absence of a offset value.
    3057             :  */
    3058          20 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
    3059             :                               CPL_UNUSED GDALDataType *peStorageType) const
    3060             : {
    3061          20 :     if (pbHasOffset)
    3062          20 :         *pbHasOffset = false;
    3063          20 :     return 0.0;
    3064             : }
    3065             : 
    3066             : /************************************************************************/
    3067             : /*                         ProcessPerChunk()                            */
    3068             : /************************************************************************/
    3069             : 
    3070             : namespace
    3071             : {
    3072             : enum class Caller
    3073             : {
    3074             :     CALLER_END_OF_LOOP,
    3075             :     CALLER_IN_LOOP,
    3076             : };
    3077             : }
    3078             : 
    3079             : /** \brief Call a user-provided function to operate on an array chunk by chunk.
    3080             :  *
    3081             :  * This method is to be used when doing operations on an array, or a subset of
    3082             :  * it, in a chunk by chunk way.
    3083             :  *
    3084             :  * @param arrayStartIdx Values representing the starting index to use
    3085             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    3086             :  *                      Array of GetDimensionCount() values. Must not be
    3087             :  *                      nullptr, unless for a zero-dimensional array.
    3088             :  *
    3089             :  * @param count         Values representing the number of values to use in
    3090             :  *                      each dimension.
    3091             :  *                      Array of GetDimensionCount() values. Must not be
    3092             :  *                      nullptr, unless for a zero-dimensional array.
    3093             :  *
    3094             :  * @param chunkSize     Values representing the chunk size in each dimension.
    3095             :  *                      Might typically the output of GetProcessingChunkSize().
    3096             :  *                      Array of GetDimensionCount() values. Must not be
    3097             :  *                      nullptr, unless for a zero-dimensional array.
    3098             :  *
    3099             :  * @param pfnFunc       User-provided function of type FuncProcessPerChunkType.
    3100             :  *                      Must NOT be nullptr.
    3101             :  *
    3102             :  * @param pUserData     Pointer to pass as the value of the pUserData argument
    3103             :  * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
    3104             :  *
    3105             :  * @return true in case of success.
    3106             :  */
    3107          88 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
    3108             :                                           const GUInt64 *count,
    3109             :                                           const size_t *chunkSize,
    3110             :                                           FuncProcessPerChunkType pfnFunc,
    3111             :                                           void *pUserData)
    3112             : {
    3113          88 :     const auto &dims = GetDimensions();
    3114          88 :     if (dims.empty())
    3115             :     {
    3116           2 :         return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
    3117             :     }
    3118             : 
    3119             :     // Sanity check
    3120          86 :     size_t nTotalChunkSize = 1;
    3121         219 :     for (size_t i = 0; i < dims.size(); i++)
    3122             :     {
    3123         140 :         const auto nSizeThisDim(dims[i]->GetSize());
    3124         140 :         if (count[i] == 0 || count[i] > nSizeThisDim ||
    3125         138 :             arrayStartIdx[i] > nSizeThisDim - count[i])
    3126             :         {
    3127           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    3128             :                      "Inconsistent arrayStartIdx[] / count[] values "
    3129             :                      "regarding array size");
    3130           4 :             return false;
    3131             :         }
    3132         270 :         if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
    3133         134 :             chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
    3134             :         {
    3135           3 :             CPLError(CE_Failure, CPLE_AppDefined,
    3136             :                      "Inconsistent chunkSize[] values");
    3137           3 :             return false;
    3138             :         }
    3139         133 :         nTotalChunkSize *= chunkSize[i];
    3140             :     }
    3141             : 
    3142          79 :     size_t dimIdx = 0;
    3143         158 :     std::vector<GUInt64> chunkArrayStartIdx(dims.size());
    3144         158 :     std::vector<size_t> chunkCount(dims.size());
    3145             : 
    3146             :     struct Stack
    3147             :     {
    3148             :         GUInt64 nBlockCounter = 0;
    3149             :         GUInt64 nBlocksMinusOne = 0;
    3150             :         size_t first_count = 0;  // only used if nBlocks > 1
    3151             :         Caller return_point = Caller::CALLER_END_OF_LOOP;
    3152             :     };
    3153             : 
    3154         158 :     std::vector<Stack> stack(dims.size());
    3155          79 :     GUInt64 iCurChunk = 0;
    3156          79 :     GUInt64 nChunkCount = 1;
    3157         211 :     for (size_t i = 0; i < dims.size(); i++)
    3158             :     {
    3159         132 :         const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
    3160         132 :         const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
    3161         132 :         stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
    3162         132 :         nChunkCount *= 1 + stack[i].nBlocksMinusOne;
    3163         132 :         if (stack[i].nBlocksMinusOne == 0)
    3164             :         {
    3165         127 :             chunkArrayStartIdx[i] = arrayStartIdx[i];
    3166         127 :             chunkCount[i] = static_cast<size_t>(count[i]);
    3167             :         }
    3168             :         else
    3169             :         {
    3170           5 :             stack[i].first_count = static_cast<size_t>(
    3171           5 :                 (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
    3172             :         }
    3173             :     }
    3174             : 
    3175          79 : lbl_next_depth:
    3176         321 :     if (dimIdx == dims.size())
    3177             :     {
    3178         112 :         ++iCurChunk;
    3179         112 :         if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
    3180             :                      iCurChunk, nChunkCount, pUserData))
    3181             :         {
    3182           1 :             return false;
    3183             :         }
    3184             :     }
    3185             :     else
    3186             :     {
    3187         209 :         if (stack[dimIdx].nBlocksMinusOne != 0)
    3188             :         {
    3189          11 :             stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
    3190          11 :             chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
    3191          11 :             chunkCount[dimIdx] = stack[dimIdx].first_count;
    3192          11 :             stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
    3193             :             while (true)
    3194             :             {
    3195          33 :                 dimIdx++;
    3196          33 :                 goto lbl_next_depth;
    3197          33 :             lbl_return_to_caller_in_loop:
    3198          33 :                 --stack[dimIdx].nBlockCounter;
    3199          33 :                 if (stack[dimIdx].nBlockCounter == 0)
    3200          11 :                     break;
    3201          22 :                 chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    3202          22 :                 chunkCount[dimIdx] = chunkSize[dimIdx];
    3203             :             }
    3204             : 
    3205          11 :             chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    3206          22 :             chunkCount[dimIdx] =
    3207          11 :                 static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
    3208          11 :                                     chunkArrayStartIdx[dimIdx]);
    3209          11 :             stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
    3210             :         }
    3211         209 :         dimIdx++;
    3212         209 :         goto lbl_next_depth;
    3213         207 :     lbl_return_to_caller_end_of_loop:
    3214         207 :         if (dimIdx == 0)
    3215          78 :             goto end;
    3216             :     }
    3217             : 
    3218         240 :     assert(dimIdx > 0);
    3219         240 :     dimIdx--;
    3220             :     // cppcheck-suppress negativeContainerIndex
    3221         240 :     switch (stack[dimIdx].return_point)
    3222             :     {
    3223         207 :         case Caller::CALLER_END_OF_LOOP:
    3224         207 :             goto lbl_return_to_caller_end_of_loop;
    3225          33 :         case Caller::CALLER_IN_LOOP:
    3226          33 :             goto lbl_return_to_caller_in_loop;
    3227             :     }
    3228          78 : end:
    3229          78 :     return true;
    3230             : }
    3231             : 
    3232             : /************************************************************************/
    3233             : /*                          GDALAttribute()                             */
    3234             : /************************************************************************/
    3235             : 
    3236             : //! @cond Doxygen_Suppress
    3237       29182 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
    3238           0 :                              CPL_UNUSED const std::string &osName)
    3239             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
    3240       29182 :     : GDALAbstractMDArray(osParentName, osName)
    3241             : #endif
    3242             : {
    3243       29182 : }
    3244             : 
    3245             : GDALAttribute::~GDALAttribute() = default;
    3246             : 
    3247             : //! @endcond
    3248             : 
    3249             : /************************************************************************/
    3250             : /*                        GetDimensionSize()                            */
    3251             : /************************************************************************/
    3252             : 
    3253             : /** Return the size of the dimensions of the attribute.
    3254             :  *
    3255             :  * This will be an empty array for a scalar (single value) attribute.
    3256             :  *
    3257             :  * This is the same as the C function GDALAttributeGetDimensionsSize().
    3258             :  */
    3259         677 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
    3260             : {
    3261         677 :     const auto &dims = GetDimensions();
    3262         677 :     std::vector<GUInt64> ret;
    3263         677 :     ret.reserve(dims.size());
    3264         830 :     for (const auto &dim : dims)
    3265         153 :         ret.push_back(dim->GetSize());
    3266         677 :     return ret;
    3267             : }
    3268             : 
    3269             : /************************************************************************/
    3270             : /*                            GDALRawResult()                           */
    3271             : /************************************************************************/
    3272             : 
    3273             : //! @cond Doxygen_Suppress
    3274         236 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
    3275         236 :                              size_t nEltCount)
    3276         472 :     : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
    3277         236 :       m_raw(raw)
    3278             : {
    3279         236 : }
    3280             : 
    3281             : //! @endcond
    3282             : 
    3283             : /************************************************************************/
    3284             : /*                            GDALRawResult()                           */
    3285             : /************************************************************************/
    3286             : 
    3287             : /** Move constructor. */
    3288           0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
    3289           0 :     : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
    3290           0 :       m_nSize(other.m_nSize), m_raw(other.m_raw)
    3291             : {
    3292           0 :     other.m_nEltCount = 0;
    3293           0 :     other.m_nSize = 0;
    3294           0 :     other.m_raw = nullptr;
    3295           0 : }
    3296             : 
    3297             : /************************************************************************/
    3298             : /*                               FreeMe()                               */
    3299             : /************************************************************************/
    3300             : 
    3301         236 : void GDALRawResult::FreeMe()
    3302             : {
    3303         236 :     if (m_raw && m_dt.NeedsFreeDynamicMemory())
    3304             :     {
    3305         122 :         GByte *pabyPtr = m_raw;
    3306         122 :         const auto nDTSize(m_dt.GetSize());
    3307         244 :         for (size_t i = 0; i < m_nEltCount; ++i)
    3308             :         {
    3309         122 :             m_dt.FreeDynamicMemory(pabyPtr);
    3310         122 :             pabyPtr += nDTSize;
    3311             :         }
    3312             :     }
    3313         236 :     VSIFree(m_raw);
    3314         236 : }
    3315             : 
    3316             : /************************************************************************/
    3317             : /*                             operator=()                              */
    3318             : /************************************************************************/
    3319             : 
    3320             : /** Move assignment. */
    3321           0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
    3322             : {
    3323           0 :     FreeMe();
    3324           0 :     m_dt = std::move(other.m_dt);
    3325           0 :     m_nEltCount = other.m_nEltCount;
    3326           0 :     m_nSize = other.m_nSize;
    3327           0 :     m_raw = other.m_raw;
    3328           0 :     other.m_nEltCount = 0;
    3329           0 :     other.m_nSize = 0;
    3330           0 :     other.m_raw = nullptr;
    3331           0 :     return *this;
    3332             : }
    3333             : 
    3334             : /************************************************************************/
    3335             : /*                         ~GDALRawResult()                             */
    3336             : /************************************************************************/
    3337             : 
    3338             : /** Destructor. */
    3339         236 : GDALRawResult::~GDALRawResult()
    3340             : {
    3341         236 :     FreeMe();
    3342         236 : }
    3343             : 
    3344             : /************************************************************************/
    3345             : /*                            StealData()                               */
    3346             : /************************************************************************/
    3347             : 
    3348             : //! @cond Doxygen_Suppress
    3349             : /** Return buffer to caller which becomes owner of it.
    3350             :  * Only to be used by GDALAttributeReadAsRaw().
    3351             :  */
    3352           6 : GByte *GDALRawResult::StealData()
    3353             : {
    3354           6 :     GByte *ret = m_raw;
    3355           6 :     m_raw = nullptr;
    3356           6 :     m_nEltCount = 0;
    3357           6 :     m_nSize = 0;
    3358           6 :     return ret;
    3359             : }
    3360             : 
    3361             : //! @endcond
    3362             : 
    3363             : /************************************************************************/
    3364             : /*                             ReadAsRaw()                              */
    3365             : /************************************************************************/
    3366             : 
    3367             : /** Return the raw value of an attribute.
    3368             :  *
    3369             :  *
    3370             :  * This is the same as the C function GDALAttributeReadAsRaw().
    3371             :  */
    3372         236 : GDALRawResult GDALAttribute::ReadAsRaw() const
    3373             : {
    3374         236 :     const auto nEltCount(GetTotalElementsCount());
    3375         236 :     const auto &dt(GetDataType());
    3376         236 :     const auto nDTSize(dt.GetSize());
    3377             :     GByte *res = static_cast<GByte *>(
    3378         236 :         VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
    3379         236 :     if (!res)
    3380           0 :         return GDALRawResult(nullptr, dt, 0);
    3381         236 :     const auto &dims = GetDimensions();
    3382         236 :     const auto nDims = GetDimensionCount();
    3383         472 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3384         472 :     std::vector<size_t> count(1 + nDims);
    3385         262 :     for (size_t i = 0; i < nDims; i++)
    3386             :     {
    3387          26 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3388             :     }
    3389         236 :     if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
    3390         236 :               &res[0], static_cast<size_t>(nEltCount * nDTSize)))
    3391             :     {
    3392           0 :         VSIFree(res);
    3393           0 :         return GDALRawResult(nullptr, dt, 0);
    3394             :     }
    3395         236 :     return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
    3396             : }
    3397             : 
    3398             : /************************************************************************/
    3399             : /*                            ReadAsString()                            */
    3400             : /************************************************************************/
    3401             : 
    3402             : /** Return the value of an attribute as a string.
    3403             :  *
    3404             :  * The returned string should not be freed, and its lifetime does not
    3405             :  * excess a next call to ReadAsString() on the same object, or the deletion
    3406             :  * of the object itself.
    3407             :  *
    3408             :  * This function will only return the first element if there are several.
    3409             :  *
    3410             :  * This is the same as the C function GDALAttributeReadAsString()
    3411             :  *
    3412             :  * @return a string, or nullptr.
    3413             :  */
    3414        2120 : const char *GDALAttribute::ReadAsString() const
    3415             : {
    3416        2120 :     const auto nDims = GetDimensionCount();
    3417        4240 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3418        4240 :     std::vector<size_t> count(1 + nDims, 1);
    3419        2120 :     char *szRet = nullptr;
    3420        2120 :     if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
    3421        2120 :               GDALExtendedDataType::CreateString(), &szRet, &szRet,
    3422        6359 :               sizeof(szRet)) ||
    3423        2119 :         szRet == nullptr)
    3424             :     {
    3425           5 :         return nullptr;
    3426             :     }
    3427        2115 :     m_osCachedVal = szRet;
    3428        2115 :     CPLFree(szRet);
    3429        2115 :     return m_osCachedVal.c_str();
    3430             : }
    3431             : 
    3432             : /************************************************************************/
    3433             : /*                            ReadAsInt()                               */
    3434             : /************************************************************************/
    3435             : 
    3436             : /** Return the value of an attribute as a integer.
    3437             :  *
    3438             :  * This function will only return the first element if there are several.
    3439             :  *
    3440             :  * It can fail if its value can not be converted to integer.
    3441             :  *
    3442             :  * This is the same as the C function GDALAttributeReadAsInt()
    3443             :  *
    3444             :  * @return a integer, or INT_MIN in case of error.
    3445             :  */
    3446         542 : int GDALAttribute::ReadAsInt() const
    3447             : {
    3448         542 :     const auto nDims = GetDimensionCount();
    3449        1084 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3450         542 :     std::vector<size_t> count(1 + nDims, 1);
    3451         542 :     int nRet = INT_MIN;
    3452         542 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3453        1084 :          GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
    3454        1084 :     return nRet;
    3455             : }
    3456             : 
    3457             : /************************************************************************/
    3458             : /*                            ReadAsInt64()                             */
    3459             : /************************************************************************/
    3460             : 
    3461             : /** Return the value of an attribute as an int64_t.
    3462             :  *
    3463             :  * This function will only return the first element if there are several.
    3464             :  *
    3465             :  * It can fail if its value can not be converted to long.
    3466             :  *
    3467             :  * This is the same as the C function GDALAttributeReadAsInt64()
    3468             :  *
    3469             :  * @return an int64_t, or INT64_MIN in case of error.
    3470             :  */
    3471         102 : int64_t GDALAttribute::ReadAsInt64() const
    3472             : {
    3473         102 :     const auto nDims = GetDimensionCount();
    3474         204 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3475         102 :     std::vector<size_t> count(1 + nDims, 1);
    3476         102 :     int64_t nRet = INT64_MIN;
    3477         102 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3478         204 :          GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
    3479         204 :     return nRet;
    3480             : }
    3481             : 
    3482             : /************************************************************************/
    3483             : /*                            ReadAsDouble()                            */
    3484             : /************************************************************************/
    3485             : 
    3486             : /** Return the value of an attribute as a double.
    3487             :  *
    3488             :  * This function will only return the first element if there are several.
    3489             :  *
    3490             :  * It can fail if its value can not be converted to double.
    3491             :  *
    3492             :  * This is the same as the C function GDALAttributeReadAsInt()
    3493             :  *
    3494             :  * @return a double value.
    3495             :  */
    3496         544 : double GDALAttribute::ReadAsDouble() const
    3497             : {
    3498         544 :     const auto nDims = GetDimensionCount();
    3499        1088 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3500         544 :     std::vector<size_t> count(1 + nDims, 1);
    3501         544 :     double dfRet = 0;
    3502         544 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3503         544 :          GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
    3504         544 :          sizeof(dfRet));
    3505        1088 :     return dfRet;
    3506             : }
    3507             : 
    3508             : /************************************************************************/
    3509             : /*                          ReadAsStringArray()                         */
    3510             : /************************************************************************/
    3511             : 
    3512             : /** Return the value of an attribute as an array of strings.
    3513             :  *
    3514             :  * This is the same as the C function GDALAttributeReadAsStringArray()
    3515             :  */
    3516         173 : CPLStringList GDALAttribute::ReadAsStringArray() const
    3517             : {
    3518         173 :     const auto nElts = GetTotalElementsCount();
    3519         173 :     if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
    3520           0 :         return CPLStringList();
    3521             :     char **papszList = static_cast<char **>(
    3522         173 :         VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
    3523         173 :     const auto &dims = GetDimensions();
    3524         173 :     const auto nDims = GetDimensionCount();
    3525         346 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3526         346 :     std::vector<size_t> count(1 + nDims);
    3527         270 :     for (size_t i = 0; i < nDims; i++)
    3528             :     {
    3529          97 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3530             :     }
    3531         173 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3532         173 :          GDALExtendedDataType::CreateString(), papszList, papszList,
    3533         173 :          sizeof(char *) * static_cast<int>(nElts));
    3534         672 :     for (int i = 0; i < static_cast<int>(nElts); i++)
    3535             :     {
    3536         499 :         if (papszList[i] == nullptr)
    3537          13 :             papszList[i] = CPLStrdup("");
    3538             :     }
    3539         173 :     return CPLStringList(papszList);
    3540             : }
    3541             : 
    3542             : /************************************************************************/
    3543             : /*                          ReadAsIntArray()                            */
    3544             : /************************************************************************/
    3545             : 
    3546             : /** Return the value of an attribute as an array of integers.
    3547             :  *
    3548             :  * This is the same as the C function GDALAttributeReadAsIntArray().
    3549             :  */
    3550          15 : std::vector<int> GDALAttribute::ReadAsIntArray() const
    3551             : {
    3552          15 :     const auto nElts = GetTotalElementsCount();
    3553             : #if SIZEOF_VOIDP == 4
    3554             :     if (nElts > static_cast<size_t>(nElts))
    3555             :         return {};
    3556             : #endif
    3557          15 :     std::vector<int> res(static_cast<size_t>(nElts));
    3558          15 :     const auto &dims = GetDimensions();
    3559          15 :     const auto nDims = GetDimensionCount();
    3560          30 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3561          30 :     std::vector<size_t> count(1 + nDims);
    3562          32 :     for (size_t i = 0; i < nDims; i++)
    3563             :     {
    3564          17 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3565             :     }
    3566          15 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3567          30 :          GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
    3568          15 :          res.size() * sizeof(res[0]));
    3569          30 :     return res;
    3570             : }
    3571             : 
    3572             : /************************************************************************/
    3573             : /*                          ReadAsInt64Array()                          */
    3574             : /************************************************************************/
    3575             : 
    3576             : /** Return the value of an attribute as an array of int64_t.
    3577             :  *
    3578             :  * This is the same as the C function GDALAttributeReadAsInt64Array().
    3579             :  */
    3580          62 : std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
    3581             : {
    3582          62 :     const auto nElts = GetTotalElementsCount();
    3583             : #if SIZEOF_VOIDP == 4
    3584             :     if (nElts > static_cast<size_t>(nElts))
    3585             :         return {};
    3586             : #endif
    3587          62 :     std::vector<int64_t> res(static_cast<size_t>(nElts));
    3588          62 :     const auto &dims = GetDimensions();
    3589          62 :     const auto nDims = GetDimensionCount();
    3590         124 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3591         124 :     std::vector<size_t> count(1 + nDims);
    3592         124 :     for (size_t i = 0; i < nDims; i++)
    3593             :     {
    3594          62 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3595             :     }
    3596          62 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3597         124 :          GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
    3598          62 :          res.size() * sizeof(res[0]));
    3599         124 :     return res;
    3600             : }
    3601             : 
    3602             : /************************************************************************/
    3603             : /*                         ReadAsDoubleArray()                          */
    3604             : /************************************************************************/
    3605             : 
    3606             : /** Return the value of an attribute as an array of double.
    3607             :  *
    3608             :  * This is the same as the C function GDALAttributeReadAsDoubleArray().
    3609             :  */
    3610          94 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
    3611             : {
    3612          94 :     const auto nElts = GetTotalElementsCount();
    3613             : #if SIZEOF_VOIDP == 4
    3614             :     if (nElts > static_cast<size_t>(nElts))
    3615             :         return {};
    3616             : #endif
    3617          94 :     std::vector<double> res(static_cast<size_t>(nElts));
    3618          94 :     const auto &dims = GetDimensions();
    3619          94 :     const auto nDims = GetDimensionCount();
    3620         188 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3621         188 :     std::vector<size_t> count(1 + nDims);
    3622         172 :     for (size_t i = 0; i < nDims; i++)
    3623             :     {
    3624          78 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3625             :     }
    3626          94 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3627         188 :          GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
    3628          94 :          res.size() * sizeof(res[0]));
    3629         188 :     return res;
    3630             : }
    3631             : 
    3632             : /************************************************************************/
    3633             : /*                               Write()                                */
    3634             : /************************************************************************/
    3635             : 
    3636             : /** Write an attribute from raw values expressed in GetDataType()
    3637             :  *
    3638             :  * The values should be provided in the type of GetDataType() and there should
    3639             :  * be exactly GetTotalElementsCount() of them.
    3640             :  * If GetDataType() is a string, each value should be a char* pointer.
    3641             :  *
    3642             :  * This is the same as the C function GDALAttributeWriteRaw().
    3643             :  *
    3644             :  * @param pabyValue Buffer of nLen bytes.
    3645             :  * @param nLen Size of pabyValue in bytes. Should be equal to
    3646             :  *             GetTotalElementsCount() * GetDataType().GetSize()
    3647             :  * @return true in case of success.
    3648             :  */
    3649         173 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
    3650             : {
    3651         173 :     if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
    3652             :     {
    3653           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3654             :                  "Length is not of expected value");
    3655           0 :         return false;
    3656             :     }
    3657         173 :     const auto &dims = GetDimensions();
    3658         173 :     const auto nDims = GetDimensionCount();
    3659         346 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3660         346 :     std::vector<size_t> count(1 + nDims);
    3661         199 :     for (size_t i = 0; i < nDims; i++)
    3662             :     {
    3663          26 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3664             :     }
    3665         173 :     return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
    3666         173 :                  pabyValue, pabyValue, nLen);
    3667             : }
    3668             : 
    3669             : /************************************************************************/
    3670             : /*                               Write()                                */
    3671             : /************************************************************************/
    3672             : 
    3673             : /** Write an attribute from a string value.
    3674             :  *
    3675             :  * Type conversion will be performed if needed. If the attribute contains
    3676             :  * multiple values, only the first one will be updated.
    3677             :  *
    3678             :  * This is the same as the C function GDALAttributeWriteString().
    3679             :  *
    3680             :  * @param pszValue Pointer to a string.
    3681             :  * @return true in case of success.
    3682             :  */
    3683         391 : bool GDALAttribute::Write(const char *pszValue)
    3684             : {
    3685         391 :     const auto nDims = GetDimensionCount();
    3686         782 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3687         391 :     std::vector<size_t> count(1 + nDims, 1);
    3688         391 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3689         782 :                  GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
    3690         782 :                  sizeof(pszValue));
    3691             : }
    3692             : 
    3693             : /************************************************************************/
    3694             : /*                              WriteInt()                              */
    3695             : /************************************************************************/
    3696             : 
    3697             : /** Write an attribute from a integer value.
    3698             :  *
    3699             :  * Type conversion will be performed if needed. If the attribute contains
    3700             :  * multiple values, only the first one will be updated.
    3701             :  *
    3702             :  * This is the same as the C function GDALAttributeWriteInt().
    3703             :  *
    3704             :  * @param nVal Value.
    3705             :  * @return true in case of success.
    3706             :  */
    3707          22 : bool GDALAttribute::WriteInt(int nVal)
    3708             : {
    3709          22 :     const auto nDims = GetDimensionCount();
    3710          44 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3711          22 :     std::vector<size_t> count(1 + nDims, 1);
    3712          22 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3713          44 :                  GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
    3714          44 :                  sizeof(nVal));
    3715             : }
    3716             : 
    3717             : /************************************************************************/
    3718             : /*                              WriteInt64()                             */
    3719             : /************************************************************************/
    3720             : 
    3721             : /** Write an attribute from an int64_t value.
    3722             :  *
    3723             :  * Type conversion will be performed if needed. If the attribute contains
    3724             :  * multiple values, only the first one will be updated.
    3725             :  *
    3726             :  * This is the same as the C function GDALAttributeWriteInt().
    3727             :  *
    3728             :  * @param nVal Value.
    3729             :  * @return true in case of success.
    3730             :  */
    3731          11 : bool GDALAttribute::WriteInt64(int64_t nVal)
    3732             : {
    3733          11 :     const auto nDims = GetDimensionCount();
    3734          22 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3735          11 :     std::vector<size_t> count(1 + nDims, 1);
    3736          11 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3737          22 :                  GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
    3738          22 :                  sizeof(nVal));
    3739             : }
    3740             : 
    3741             : /************************************************************************/
    3742             : /*                                Write()                               */
    3743             : /************************************************************************/
    3744             : 
    3745             : /** Write an attribute from a double value.
    3746             :  *
    3747             :  * Type conversion will be performed if needed. If the attribute contains
    3748             :  * multiple values, only the first one will be updated.
    3749             :  *
    3750             :  * This is the same as the C function GDALAttributeWriteDouble().
    3751             :  *
    3752             :  * @param dfVal Value.
    3753             :  * @return true in case of success.
    3754             :  */
    3755          39 : bool GDALAttribute::Write(double dfVal)
    3756             : {
    3757          39 :     const auto nDims = GetDimensionCount();
    3758          78 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3759          39 :     std::vector<size_t> count(1 + nDims, 1);
    3760          39 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3761          78 :                  GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
    3762          78 :                  sizeof(dfVal));
    3763             : }
    3764             : 
    3765             : /************************************************************************/
    3766             : /*                                Write()                               */
    3767             : /************************************************************************/
    3768             : 
    3769             : /** Write an attribute from an array of strings.
    3770             :  *
    3771             :  * Type conversion will be performed if needed.
    3772             :  *
    3773             :  * Exactly GetTotalElementsCount() strings must be provided
    3774             :  *
    3775             :  * This is the same as the C function GDALAttributeWriteStringArray().
    3776             :  *
    3777             :  * @param vals Array of strings.
    3778             :  * @return true in case of success.
    3779             :  */
    3780           8 : bool GDALAttribute::Write(CSLConstList vals)
    3781             : {
    3782           8 :     if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
    3783             :     {
    3784           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3785           1 :         return false;
    3786             :     }
    3787           7 :     const auto nDims = GetDimensionCount();
    3788          14 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3789           7 :     std::vector<size_t> count(1 + nDims);
    3790           7 :     const auto &dims = GetDimensions();
    3791          15 :     for (size_t i = 0; i < nDims; i++)
    3792           8 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3793           7 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3794           7 :                  GDALExtendedDataType::CreateString(), vals, vals,
    3795          14 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
    3796             : }
    3797             : 
    3798             : /************************************************************************/
    3799             : /*                                Write()                               */
    3800             : /************************************************************************/
    3801             : 
    3802             : /** Write an attribute from an array of int.
    3803             :  *
    3804             :  * Type conversion will be performed if needed.
    3805             :  *
    3806             :  * Exactly GetTotalElementsCount() strings must be provided
    3807             :  *
    3808             :  * This is the same as the C function GDALAttributeWriteIntArray()
    3809             :  *
    3810             :  * @param vals Array of int.
    3811             :  * @param nVals Should be equal to GetTotalElementsCount().
    3812             :  * @return true in case of success.
    3813             :  */
    3814          11 : bool GDALAttribute::Write(const int *vals, size_t nVals)
    3815             : {
    3816          11 :     if (nVals != GetTotalElementsCount())
    3817             :     {
    3818           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3819           1 :         return false;
    3820             :     }
    3821          10 :     const auto nDims = GetDimensionCount();
    3822          20 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3823          10 :     std::vector<size_t> count(1 + nDims);
    3824          10 :     const auto &dims = GetDimensions();
    3825          20 :     for (size_t i = 0; i < nDims; i++)
    3826          10 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3827          10 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3828          10 :                  GDALExtendedDataType::Create(GDT_Int32), vals, vals,
    3829          20 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
    3830             : }
    3831             : 
    3832             : /************************************************************************/
    3833             : /*                                Write()                               */
    3834             : /************************************************************************/
    3835             : 
    3836             : /** Write an attribute from an array of int64_t.
    3837             :  *
    3838             :  * Type conversion will be performed if needed.
    3839             :  *
    3840             :  * Exactly GetTotalElementsCount() strings must be provided
    3841             :  *
    3842             :  * This is the same as the C function GDALAttributeWriteLongArray()
    3843             :  *
    3844             :  * @param vals Array of int64_t.
    3845             :  * @param nVals Should be equal to GetTotalElementsCount().
    3846             :  * @return true in case of success.
    3847             :  */
    3848          10 : bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
    3849             : {
    3850          10 :     if (nVals != GetTotalElementsCount())
    3851             :     {
    3852           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3853           0 :         return false;
    3854             :     }
    3855          10 :     const auto nDims = GetDimensionCount();
    3856          20 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3857          10 :     std::vector<size_t> count(1 + nDims);
    3858          10 :     const auto &dims = GetDimensions();
    3859          20 :     for (size_t i = 0; i < nDims; i++)
    3860          10 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3861          10 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3862          10 :                  GDALExtendedDataType::Create(GDT_Int64), vals, vals,
    3863          10 :                  static_cast<size_t>(GetTotalElementsCount()) *
    3864          10 :                      sizeof(int64_t));
    3865             : }
    3866             : 
    3867             : /************************************************************************/
    3868             : /*                                Write()                               */
    3869             : /************************************************************************/
    3870             : 
    3871             : /** Write an attribute from an array of double.
    3872             :  *
    3873             :  * Type conversion will be performed if needed.
    3874             :  *
    3875             :  * Exactly GetTotalElementsCount() strings must be provided
    3876             :  *
    3877             :  * This is the same as the C function GDALAttributeWriteDoubleArray()
    3878             :  *
    3879             :  * @param vals Array of double.
    3880             :  * @param nVals Should be equal to GetTotalElementsCount().
    3881             :  * @return true in case of success.
    3882             :  */
    3883           7 : bool GDALAttribute::Write(const double *vals, size_t nVals)
    3884             : {
    3885           7 :     if (nVals != GetTotalElementsCount())
    3886             :     {
    3887           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3888           1 :         return false;
    3889             :     }
    3890           6 :     const auto nDims = GetDimensionCount();
    3891          12 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3892           6 :     std::vector<size_t> count(1 + nDims);
    3893           6 :     const auto &dims = GetDimensions();
    3894          13 :     for (size_t i = 0; i < nDims; i++)
    3895           7 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3896           6 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3897           6 :                  GDALExtendedDataType::Create(GDT_Float64), vals, vals,
    3898          12 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
    3899             : }
    3900             : 
    3901             : /************************************************************************/
    3902             : /*                           GDALMDArray()                              */
    3903             : /************************************************************************/
    3904             : 
    3905             : //! @cond Doxygen_Suppress
    3906        7590 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
    3907             :                          CPL_UNUSED const std::string &osName,
    3908           0 :                          const std::string &osContext)
    3909             :     :
    3910             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
    3911             :       GDALAbstractMDArray(osParentName, osName),
    3912             : #endif
    3913        7590 :       m_osContext(osContext)
    3914             : {
    3915        7590 : }
    3916             : 
    3917             : //! @endcond
    3918             : 
    3919             : /************************************************************************/
    3920             : /*                           GetTotalCopyCost()                         */
    3921             : /************************************************************************/
    3922             : 
    3923             : /** Return a total "cost" to copy the array.
    3924             :  *
    3925             :  * Used as a parameter for CopyFrom()
    3926             :  */
    3927          73 : GUInt64 GDALMDArray::GetTotalCopyCost() const
    3928             : {
    3929         146 :     return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
    3930         146 :            GetTotalElementsCount() * GetDataType().GetSize();
    3931             : }
    3932             : 
    3933             : /************************************************************************/
    3934             : /*                       CopyFromAllExceptValues()                      */
    3935             : /************************************************************************/
    3936             : 
    3937             : //! @cond Doxygen_Suppress
    3938             : 
    3939         214 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
    3940             :                                           bool bStrict, GUInt64 &nCurCost,
    3941             :                                           const GUInt64 nTotalCost,
    3942             :                                           GDALProgressFunc pfnProgress,
    3943             :                                           void *pProgressData)
    3944             : {
    3945             :     // Nodata setting must be one of the first things done for TileDB
    3946         214 :     const void *pNoData = poSrcArray->GetRawNoDataValue();
    3947         214 :     if (pNoData && poSrcArray->GetDataType() == GetDataType())
    3948             :     {
    3949          13 :         SetRawNoDataValue(pNoData);
    3950             :     }
    3951             : 
    3952         214 :     const bool bThisIsUnscaledArray =
    3953         214 :         dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
    3954         428 :     auto attrs = poSrcArray->GetAttributes();
    3955         297 :     for (const auto &attr : attrs)
    3956             :     {
    3957          83 :         const auto &osAttrName = attr->GetName();
    3958          83 :         if (bThisIsUnscaledArray)
    3959             :         {
    3960           6 :             if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
    3961           7 :                 osAttrName == "valid_min" || osAttrName == "valid_max" ||
    3962           1 :                 osAttrName == "valid_range")
    3963             :             {
    3964           1 :                 continue;
    3965             :             }
    3966             :         }
    3967             : 
    3968          82 :         auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
    3969         164 :                                        attr->GetDataType());
    3970          82 :         if (!dstAttr)
    3971             :         {
    3972           0 :             if (bStrict)
    3973           0 :                 return false;
    3974           0 :             continue;
    3975             :         }
    3976          82 :         auto raw = attr->ReadAsRaw();
    3977          82 :         if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
    3978           0 :             return false;
    3979             :     }
    3980         214 :     if (!attrs.empty())
    3981             :     {
    3982          47 :         nCurCost += attrs.size() * GDALAttribute::COPY_COST;
    3983          81 :         if (pfnProgress &&
    3984          34 :             !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    3985           0 :             return false;
    3986             :     }
    3987             : 
    3988         214 :     auto srcSRS = poSrcArray->GetSpatialRef();
    3989         214 :     if (srcSRS)
    3990             :     {
    3991          22 :         SetSpatialRef(srcSRS.get());
    3992             :     }
    3993             : 
    3994         214 :     const std::string &osUnit(poSrcArray->GetUnit());
    3995         214 :     if (!osUnit.empty())
    3996             :     {
    3997          24 :         SetUnit(osUnit);
    3998             :     }
    3999             : 
    4000         214 :     bool bGotValue = false;
    4001         214 :     GDALDataType eOffsetStorageType = GDT_Unknown;
    4002             :     const double dfOffset =
    4003         214 :         poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
    4004         214 :     if (bGotValue)
    4005             :     {
    4006           3 :         SetOffset(dfOffset, eOffsetStorageType);
    4007             :     }
    4008             : 
    4009         214 :     bGotValue = false;
    4010         214 :     GDALDataType eScaleStorageType = GDT_Unknown;
    4011         214 :     const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
    4012         214 :     if (bGotValue)
    4013             :     {
    4014           3 :         SetScale(dfScale, eScaleStorageType);
    4015             :     }
    4016             : 
    4017         214 :     return true;
    4018             : }
    4019             : 
    4020             : //! @endcond
    4021             : 
    4022             : /************************************************************************/
    4023             : /*                               CopyFrom()                             */
    4024             : /************************************************************************/
    4025             : 
    4026             : /** Copy the content of an array into a new (generally empty) array.
    4027             :  *
    4028             :  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
    4029             :  *                   of some output drivers this is not recommended)
    4030             :  * @param poSrcArray Source array. Should NOT be nullptr.
    4031             :  * @param bStrict Whether to enable strict mode. In strict mode, any error will
    4032             :  *                stop the copy. In relaxed mode, the copy will be attempted to
    4033             :  *                be pursued.
    4034             :  * @param nCurCost  Should be provided as a variable initially set to 0.
    4035             :  * @param nTotalCost Total cost from GetTotalCopyCost().
    4036             :  * @param pfnProgress Progress callback, or nullptr.
    4037             :  * @param pProgressData Progress user data, or nulptr.
    4038             :  *
    4039             :  * @return true in case of success (or partial success if bStrict == false).
    4040             :  */
    4041          68 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
    4042             :                            const GDALMDArray *poSrcArray, bool bStrict,
    4043             :                            GUInt64 &nCurCost, const GUInt64 nTotalCost,
    4044             :                            GDALProgressFunc pfnProgress, void *pProgressData)
    4045             : {
    4046          68 :     if (pfnProgress == nullptr)
    4047           4 :         pfnProgress = GDALDummyProgress;
    4048             : 
    4049          68 :     nCurCost += GDALMDArray::COPY_COST;
    4050             : 
    4051          68 :     if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
    4052             :                                  pfnProgress, pProgressData))
    4053             :     {
    4054           0 :         return false;
    4055             :     }
    4056             : 
    4057          68 :     const auto &dims = poSrcArray->GetDimensions();
    4058          68 :     const auto nDTSize = poSrcArray->GetDataType().GetSize();
    4059          68 :     if (dims.empty())
    4060             :     {
    4061           2 :         std::vector<GByte> abyTmp(nDTSize);
    4062           2 :         if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
    4063           2 :                                GetDataType(), &abyTmp[0]) &&
    4064           2 :               Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
    4065           4 :                     &abyTmp[0])) &&
    4066             :             bStrict)
    4067             :         {
    4068           0 :             return false;
    4069             :         }
    4070           2 :         nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
    4071           2 :         if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    4072           0 :             return false;
    4073             :     }
    4074             :     else
    4075             :     {
    4076          66 :         std::vector<GUInt64> arrayStartIdx(dims.size());
    4077          66 :         std::vector<GUInt64> count(dims.size());
    4078         172 :         for (size_t i = 0; i < dims.size(); i++)
    4079             :         {
    4080         106 :             count[i] = static_cast<size_t>(dims[i]->GetSize());
    4081             :         }
    4082             : 
    4083             :         struct CopyFunc
    4084             :         {
    4085             :             GDALMDArray *poDstArray = nullptr;
    4086             :             std::vector<GByte> abyTmp{};
    4087             :             GDALProgressFunc pfnProgress = nullptr;
    4088             :             void *pProgressData = nullptr;
    4089             :             GUInt64 nCurCost = 0;
    4090             :             GUInt64 nTotalCost = 0;
    4091             :             GUInt64 nTotalBytesThisArray = 0;
    4092             :             bool bStop = false;
    4093             : 
    4094          84 :             static bool f(GDALAbstractMDArray *l_poSrcArray,
    4095             :                           const GUInt64 *chunkArrayStartIdx,
    4096             :                           const size_t *chunkCount, GUInt64 iCurChunk,
    4097             :                           GUInt64 nChunkCount, void *pUserData)
    4098             :             {
    4099          84 :                 const auto &dt(l_poSrcArray->GetDataType());
    4100          84 :                 auto data = static_cast<CopyFunc *>(pUserData);
    4101          84 :                 auto poDstArray = data->poDstArray;
    4102          84 :                 if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
    4103          84 :                                         nullptr, dt, &data->abyTmp[0]))
    4104             :                 {
    4105           1 :                     return false;
    4106             :                 }
    4107             :                 bool bRet =
    4108          83 :                     poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
    4109          83 :                                       nullptr, dt, &data->abyTmp[0]);
    4110          83 :                 if (dt.NeedsFreeDynamicMemory())
    4111             :                 {
    4112           5 :                     const auto l_nDTSize = dt.GetSize();
    4113           5 :                     GByte *ptr = &data->abyTmp[0];
    4114           5 :                     const size_t l_nDims(l_poSrcArray->GetDimensionCount());
    4115           5 :                     size_t nEltCount = 1;
    4116          10 :                     for (size_t i = 0; i < l_nDims; ++i)
    4117             :                     {
    4118           5 :                         nEltCount *= chunkCount[i];
    4119             :                     }
    4120          22 :                     for (size_t i = 0; i < nEltCount; i++)
    4121             :                     {
    4122          17 :                         dt.FreeDynamicMemory(ptr);
    4123          17 :                         ptr += l_nDTSize;
    4124             :                     }
    4125             :                 }
    4126          83 :                 if (!bRet)
    4127             :                 {
    4128           0 :                     return false;
    4129             :                 }
    4130             : 
    4131          83 :                 double dfCurCost =
    4132          83 :                     double(data->nCurCost) + double(iCurChunk) / nChunkCount *
    4133          83 :                                                  data->nTotalBytesThisArray;
    4134          83 :                 if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
    4135             :                                        data->pProgressData))
    4136             :                 {
    4137           0 :                     data->bStop = true;
    4138           0 :                     return false;
    4139             :                 }
    4140             : 
    4141          83 :                 return true;
    4142             :             }
    4143             :         };
    4144             : 
    4145          66 :         CopyFunc copyFunc;
    4146          66 :         copyFunc.poDstArray = this;
    4147          66 :         copyFunc.nCurCost = nCurCost;
    4148          66 :         copyFunc.nTotalCost = nTotalCost;
    4149          66 :         copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
    4150          66 :         copyFunc.pfnProgress = pfnProgress;
    4151          66 :         copyFunc.pProgressData = pProgressData;
    4152             :         const char *pszSwathSize =
    4153          66 :             CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
    4154             :         const size_t nMaxChunkSize =
    4155             :             pszSwathSize
    4156          66 :                 ? static_cast<size_t>(
    4157           1 :                       std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    4158           1 :                                CPLAtoGIntBig(pszSwathSize)))
    4159             :                 : static_cast<size_t>(
    4160          65 :                       std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    4161          65 :                                GDALGetCacheMax64() / 4));
    4162          66 :         const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
    4163          66 :         size_t nRealChunkSize = nDTSize;
    4164         172 :         for (const auto &nChunkSize : anChunkSizes)
    4165             :         {
    4166         106 :             nRealChunkSize *= nChunkSize;
    4167             :         }
    4168             :         try
    4169             :         {
    4170          66 :             copyFunc.abyTmp.resize(nRealChunkSize);
    4171             :         }
    4172           0 :         catch (const std::exception &)
    4173             :         {
    4174           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    4175             :                      "Cannot allocate temporary buffer");
    4176           0 :             nCurCost += copyFunc.nTotalBytesThisArray;
    4177           0 :             return false;
    4178             :         }
    4179         197 :         if (copyFunc.nTotalBytesThisArray != 0 &&
    4180          65 :             !const_cast<GDALMDArray *>(poSrcArray)
    4181          65 :                  ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
    4182             :                                    anChunkSizes.data(), CopyFunc::f,
    4183         132 :                                    &copyFunc) &&
    4184           1 :             (bStrict || copyFunc.bStop))
    4185             :         {
    4186           0 :             nCurCost += copyFunc.nTotalBytesThisArray;
    4187           0 :             return false;
    4188             :         }
    4189          66 :         nCurCost += copyFunc.nTotalBytesThisArray;
    4190             :     }
    4191             : 
    4192          68 :     return true;
    4193             : }
    4194             : 
    4195             : /************************************************************************/
    4196             : /*                         GetStructuralInfo()                          */
    4197             : /************************************************************************/
    4198             : 
    4199             : /** Return structural information on the array.
    4200             :  *
    4201             :  * This may be the compression, etc..
    4202             :  *
    4203             :  * The return value should not be freed and is valid until GDALMDArray is
    4204             :  * released or this function called again.
    4205             :  *
    4206             :  * This is the same as the C function GDALMDArrayGetStructuralInfo().
    4207             :  */
    4208          95 : CSLConstList GDALMDArray::GetStructuralInfo() const
    4209             : {
    4210          95 :     return nullptr;
    4211             : }
    4212             : 
    4213             : /************************************************************************/
    4214             : /*                          AdviseRead()                                */
    4215             : /************************************************************************/
    4216             : 
    4217             : /** Advise driver of upcoming read requests.
    4218             :  *
    4219             :  * Some GDAL drivers operate more efficiently if they know in advance what
    4220             :  * set of upcoming read requests will be made.  The AdviseRead() method allows
    4221             :  * an application to notify the driver of the region of interest.
    4222             :  *
    4223             :  * Many drivers just ignore the AdviseRead() call, but it can dramatically
    4224             :  * accelerate access via some drivers. One such case is when reading through
    4225             :  * a DAP dataset with the netCDF driver (a in-memory cache array is then created
    4226             :  * with the region of interest defined by AdviseRead())
    4227             :  *
    4228             :  * This is the same as the C function GDALMDArrayAdviseRead().
    4229             :  *
    4230             :  * @param arrayStartIdx Values representing the starting index to read
    4231             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    4232             :  *                      Array of GetDimensionCount() values.
    4233             :  *                      Can be nullptr as a synonymous for [0 for i in
    4234             :  * range(GetDimensionCount() ]
    4235             :  *
    4236             :  * @param count         Values representing the number of values to extract in
    4237             :  *                      each dimension.
    4238             :  *                      Array of GetDimensionCount() values.
    4239             :  *                      Can be nullptr as a synonymous for
    4240             :  *                      [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
    4241             :  * range(GetDimensionCount() ]
    4242             :  *
    4243             :  * @param papszOptions Driver specific options, or nullptr. Consult driver
    4244             :  * documentation.
    4245             :  *
    4246             :  * @return true in case of success (ignoring the advice is a success)
    4247             :  *
    4248             :  * @since GDAL 3.2
    4249             :  */
    4250          25 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    4251             :                              CSLConstList papszOptions) const
    4252             : {
    4253          25 :     const auto nDimCount = GetDimensionCount();
    4254          25 :     if (nDimCount == 0)
    4255           2 :         return true;
    4256             : 
    4257          46 :     std::vector<GUInt64> tmp_arrayStartIdx;
    4258          23 :     if (arrayStartIdx == nullptr)
    4259             :     {
    4260           0 :         tmp_arrayStartIdx.resize(nDimCount);
    4261           0 :         arrayStartIdx = tmp_arrayStartIdx.data();
    4262             :     }
    4263             : 
    4264          46 :     std::vector<size_t> tmp_count;
    4265          23 :     if (count == nullptr)
    4266             :     {
    4267           0 :         tmp_count.resize(nDimCount);
    4268           0 :         const auto &dims = GetDimensions();
    4269           0 :         for (size_t i = 0; i < nDimCount; i++)
    4270             :         {
    4271           0 :             const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
    4272             : #if SIZEOF_VOIDP < 8
    4273             :             if (nSize != static_cast<size_t>(nSize))
    4274             :             {
    4275             :                 CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
    4276             :                 return false;
    4277             :             }
    4278             : #endif
    4279           0 :             tmp_count[i] = static_cast<size_t>(nSize);
    4280             :         }
    4281           0 :         count = tmp_count.data();
    4282             :     }
    4283             : 
    4284          46 :     std::vector<GInt64> tmp_arrayStep;
    4285          46 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    4286          23 :     const GInt64 *arrayStep = nullptr;
    4287          23 :     const GPtrDiff_t *bufferStride = nullptr;
    4288          23 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    4289          46 :                               GDALExtendedDataType::Create(GDT_Unknown),
    4290             :                               nullptr, nullptr, 0, tmp_arrayStep,
    4291             :                               tmp_bufferStride))
    4292             :     {
    4293           1 :         return false;
    4294             :     }
    4295             : 
    4296          22 :     return IAdviseRead(arrayStartIdx, count, papszOptions);
    4297             : }
    4298             : 
    4299             : /************************************************************************/
    4300             : /*                             IAdviseRead()                            */
    4301             : /************************************************************************/
    4302             : 
    4303             : //! @cond Doxygen_Suppress
    4304           3 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
    4305             :                               CSLConstList /* papszOptions*/) const
    4306             : {
    4307           3 :     return true;
    4308             : }
    4309             : 
    4310             : //! @endcond
    4311             : 
    4312             : /************************************************************************/
    4313             : /*                            MassageName()                             */
    4314             : /************************************************************************/
    4315             : 
    4316             : //! @cond Doxygen_Suppress
    4317          32 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
    4318             : {
    4319          32 :     std::string ret;
    4320         604 :     for (const char ch : inputName)
    4321             :     {
    4322         572 :         if (!isalnum(static_cast<unsigned char>(ch)))
    4323         138 :             ret += '_';
    4324             :         else
    4325         434 :             ret += ch;
    4326             :     }
    4327          32 :     return ret;
    4328             : }
    4329             : 
    4330             : //! @endcond
    4331             : 
    4332             : /************************************************************************/
    4333             : /*                         GetCacheRootGroup()                          */
    4334             : /************************************************************************/
    4335             : 
    4336             : //! @cond Doxygen_Suppress
    4337             : std::shared_ptr<GDALGroup>
    4338        1651 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
    4339             :                                std::string &osCacheFilenameOut) const
    4340             : {
    4341        1651 :     const auto &osFilename = GetFilename();
    4342        1651 :     if (osFilename.empty())
    4343             :     {
    4344           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4345             :                  "Cannot cache an array with an empty filename");
    4346           1 :         return nullptr;
    4347             :     }
    4348             : 
    4349        1650 :     osCacheFilenameOut = osFilename + ".gmac";
    4350        1650 :     if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
    4351             :     {
    4352           0 :         const auto nPosQuestionMark = osFilename.find('?');
    4353           0 :         if (nPosQuestionMark != std::string::npos)
    4354             :         {
    4355             :             osCacheFilenameOut =
    4356           0 :                 osFilename.substr(0, nPosQuestionMark)
    4357           0 :                     .append(".gmac")
    4358           0 :                     .append(osFilename.substr(nPosQuestionMark));
    4359             :         }
    4360             :     }
    4361        1650 :     const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
    4362        1650 :     if (pszProxy != nullptr)
    4363           7 :         osCacheFilenameOut = pszProxy;
    4364             : 
    4365        1650 :     std::unique_ptr<GDALDataset> poDS;
    4366             :     VSIStatBufL sStat;
    4367        1650 :     if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
    4368             :     {
    4369          28 :         poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
    4370             :                                      GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
    4371             :                                      nullptr, nullptr, nullptr));
    4372             :     }
    4373        1650 :     if (poDS)
    4374             :     {
    4375          28 :         CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
    4376          28 :         return poDS->GetRootGroup();
    4377             :     }
    4378             : 
    4379        1622 :     if (bCanCreate)
    4380             :     {
    4381           4 :         const char *pszDrvName = "netCDF";
    4382           4 :         GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
    4383           4 :         if (poDrv == nullptr)
    4384             :         {
    4385           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
    4386             :                      pszDrvName);
    4387           0 :             return nullptr;
    4388             :         }
    4389             :         {
    4390           8 :             CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
    4391           8 :             CPLErrorStateBackuper oErrorStateBackuper;
    4392           4 :             poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
    4393             :                                                      nullptr, nullptr));
    4394             :         }
    4395           4 :         if (!poDS)
    4396             :         {
    4397           1 :             pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
    4398           1 :             if (pszProxy)
    4399             :             {
    4400           1 :                 osCacheFilenameOut = pszProxy;
    4401           1 :                 poDS.reset(poDrv->CreateMultiDimensional(
    4402             :                     osCacheFilenameOut.c_str(), nullptr, nullptr));
    4403             :             }
    4404             :         }
    4405           4 :         if (poDS)
    4406             :         {
    4407           4 :             CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
    4408           4 :             return poDS->GetRootGroup();
    4409             :         }
    4410             :         else
    4411             :         {
    4412           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4413             :                      "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
    4414             :                      "configuration option to write the cache in "
    4415             :                      "another directory",
    4416             :                      osCacheFilenameOut.c_str());
    4417             :         }
    4418             :     }
    4419             : 
    4420        1618 :     return nullptr;
    4421             : }
    4422             : 
    4423             : //! @endcond
    4424             : 
    4425             : /************************************************************************/
    4426             : /*                              Cache()                                 */
    4427             : /************************************************************************/
    4428             : 
    4429             : /** Cache the content of the array into an auxiliary filename.
    4430             :  *
    4431             :  * The main purpose of this method is to be able to cache views that are
    4432             :  * expensive to compute, such as transposed arrays.
    4433             :  *
    4434             :  * The array will be stored in a file whose name is the one of
    4435             :  * GetFilename(), with an extra .gmac extension (stands for GDAL
    4436             :  * Multidimensional Array Cache). The cache is a netCDF dataset.
    4437             :  *
    4438             :  * If the .gmac file cannot be written next to the dataset, the
    4439             :  * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
    4440             :  * directory.
    4441             :  *
    4442             :  * The GDALMDArray::Read() method will automatically use the cache when it
    4443             :  * exists. There is no timestamp checks between the source array and the cached
    4444             :  * array. If the source arrays changes, the cache must be manually deleted.
    4445             :  *
    4446             :  * This is the same as the C function GDALMDArrayCache()
    4447             :  *
    4448             :  * @note Driver implementation: optionally implemented.
    4449             :  *
    4450             :  * @param papszOptions List of options, null terminated, or NULL. Currently
    4451             :  *                     the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
    4452             :  *                     to specify the block size of the cached array.
    4453             :  * @return true in case of success.
    4454             :  */
    4455           7 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
    4456             : {
    4457          14 :     std::string osCacheFilename;
    4458          14 :     auto poRG = GetCacheRootGroup(true, osCacheFilename);
    4459           7 :     if (!poRG)
    4460           1 :         return false;
    4461             : 
    4462          12 :     const std::string osCachedArrayName(MassageName(GetFullName()));
    4463           6 :     if (poRG->OpenMDArray(osCachedArrayName))
    4464             :     {
    4465           2 :         CPLError(CE_Failure, CPLE_NotSupported,
    4466             :                  "An array with same name %s already exists in %s",
    4467             :                  osCachedArrayName.c_str(), osCacheFilename.c_str());
    4468           2 :         return false;
    4469             :     }
    4470             : 
    4471           8 :     CPLStringList aosOptions;
    4472           4 :     aosOptions.SetNameValue("COMPRESS", "DEFLATE");
    4473           4 :     const auto &aoDims = GetDimensions();
    4474           8 :     std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
    4475           4 :     if (!aoDims.empty())
    4476             :     {
    4477             :         std::string osBlockSize(
    4478           4 :             CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
    4479           4 :         if (osBlockSize.empty())
    4480             :         {
    4481           6 :             const auto anBlockSize = GetBlockSize();
    4482           3 :             int idxDim = 0;
    4483          10 :             for (auto nBlockSize : anBlockSize)
    4484             :             {
    4485           7 :                 if (idxDim > 0)
    4486           4 :                     osBlockSize += ',';
    4487           7 :                 if (nBlockSize == 0)
    4488           7 :                     nBlockSize = 256;
    4489           7 :                 nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
    4490             :                 osBlockSize +=
    4491           7 :                     std::to_string(static_cast<uint64_t>(nBlockSize));
    4492           7 :                 idxDim++;
    4493             :             }
    4494             :         }
    4495           4 :         aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
    4496             : 
    4497           4 :         int idxDim = 0;
    4498          13 :         for (const auto &poDim : aoDims)
    4499             :         {
    4500           9 :             auto poNewDim = poRG->CreateDimension(
    4501          18 :                 osCachedArrayName + '_' + std::to_string(idxDim),
    4502          18 :                 poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
    4503           9 :             if (!poNewDim)
    4504           0 :                 return false;
    4505           9 :             aoNewDims.emplace_back(poNewDim);
    4506           9 :             idxDim++;
    4507             :         }
    4508             :     }
    4509             : 
    4510           4 :     auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
    4511           8 :                                              GetDataType(), aosOptions.List());
    4512           4 :     if (!poCachedArray)
    4513             :     {
    4514           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
    4515             :                  osCachedArrayName.c_str(), osCacheFilename.c_str());
    4516           0 :         return false;
    4517             :     }
    4518             : 
    4519           4 :     GUInt64 nCost = 0;
    4520           8 :     return poCachedArray->CopyFrom(nullptr, this,
    4521             :                                    false,  // strict
    4522           4 :                                    nCost, GetTotalCopyCost(), nullptr, nullptr);
    4523             : }
    4524             : 
    4525             : /************************************************************************/
    4526             : /*                               Read()                                 */
    4527             : /************************************************************************/
    4528             : 
    4529        4454 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
    4530             :                        const GInt64 *arrayStep,         // step in elements
    4531             :                        const GPtrDiff_t *bufferStride,  // stride in elements
    4532             :                        const GDALExtendedDataType &bufferDataType,
    4533             :                        void *pDstBuffer, const void *pDstBufferAllocStart,
    4534             :                        size_t nDstBufferAllocSize) const
    4535             : {
    4536        4454 :     if (!m_bHasTriedCachedArray)
    4537             :     {
    4538        2108 :         m_bHasTriedCachedArray = true;
    4539        2108 :         if (IsCacheable())
    4540             :         {
    4541        2108 :             const auto &osFilename = GetFilename();
    4542        3553 :             if (!osFilename.empty() &&
    4543        3553 :                 !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
    4544             :             {
    4545        2870 :                 std::string osCacheFilename;
    4546        2870 :                 auto poRG = GetCacheRootGroup(false, osCacheFilename);
    4547        1435 :                 if (poRG)
    4548             :                 {
    4549             :                     const std::string osCachedArrayName(
    4550          32 :                         MassageName(GetFullName()));
    4551          16 :                     m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
    4552          16 :                     if (m_poCachedArray)
    4553             :                     {
    4554           6 :                         const auto &dims = GetDimensions();
    4555             :                         const auto &cachedDims =
    4556           6 :                             m_poCachedArray->GetDimensions();
    4557           6 :                         const size_t nDims = dims.size();
    4558             :                         bool ok =
    4559          12 :                             m_poCachedArray->GetDataType() == GetDataType() &&
    4560           6 :                             cachedDims.size() == nDims;
    4561          19 :                         for (size_t i = 0; ok && i < nDims; ++i)
    4562             :                         {
    4563          13 :                             ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
    4564             :                         }
    4565           6 :                         if (ok)
    4566             :                         {
    4567           6 :                             CPLDebug("GDAL", "Cached array for %s found in %s",
    4568             :                                      osCachedArrayName.c_str(),
    4569             :                                      osCacheFilename.c_str());
    4570             :                         }
    4571             :                         else
    4572             :                         {
    4573           0 :                             CPLError(CE_Warning, CPLE_AppDefined,
    4574             :                                      "Cached array %s in %s has incompatible "
    4575             :                                      "characteristics with current array.",
    4576             :                                      osCachedArrayName.c_str(),
    4577             :                                      osCacheFilename.c_str());
    4578           0 :                             m_poCachedArray.reset();
    4579             :                         }
    4580             :                     }
    4581             :                 }
    4582             :             }
    4583             :         }
    4584             :     }
    4585             : 
    4586        4454 :     const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
    4587        4454 :     if (!array->GetDataType().CanConvertTo(bufferDataType))
    4588             :     {
    4589           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4590             :                  "Array data type is not convertible to buffer data type");
    4591           0 :         return false;
    4592             :     }
    4593             : 
    4594        8908 :     std::vector<GInt64> tmp_arrayStep;
    4595        8908 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    4596        4454 :     if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
    4597             :                                      bufferStride, bufferDataType, pDstBuffer,
    4598             :                                      pDstBufferAllocStart, nDstBufferAllocSize,
    4599             :                                      tmp_arrayStep, tmp_bufferStride))
    4600             :     {
    4601           9 :         return false;
    4602             :     }
    4603             : 
    4604        4445 :     return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
    4605        4445 :                         bufferDataType, pDstBuffer);
    4606             : }
    4607             : 
    4608             : /************************************************************************/
    4609             : /*                          GetRootGroup()                              */
    4610             : /************************************************************************/
    4611             : 
    4612             : /** Return the root group to which this arrays belongs too.
    4613             :  *
    4614             :  * Note that arrays may be free standing and some drivers may not implement
    4615             :  * this method, hence nullptr may be returned.
    4616             :  *
    4617             :  * It is used internally by the GetResampled() method to detect if GLT
    4618             :  * orthorectification is available.
    4619             :  *
    4620             :  * @return the root group, or nullptr.
    4621             :  * @since GDAL 3.8
    4622             :  */
    4623           0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
    4624             : {
    4625           0 :     return nullptr;
    4626             : }
    4627             : 
    4628             : //! @cond Doxygen_Suppress
    4629             : 
    4630             : /************************************************************************/
    4631             : /*                       IsTransposedRequest()                          */
    4632             : /************************************************************************/
    4633             : 
    4634        1005 : bool GDALMDArray::IsTransposedRequest(
    4635             :     const size_t *count,
    4636             :     const GPtrDiff_t *bufferStride) const  // stride in elements
    4637             : {
    4638             :     /*
    4639             :     For example:
    4640             :     count = [2,3,4]
    4641             :     strides = [12, 4, 1]            (2-1)*12+(3-1)*4+(4-1)*1=23   ==> row major
    4642             :     stride [12, 1, 3]            (2-1)*12+(3-1)*1+(4-1)*3=23   ==>
    4643             :     (axis[0],axis[2],axis[1]) transposition [1, 8, 2]             (2-1)*1+
    4644             :     (3-1)*8+(4-1)*2=23   ==> (axis[2],axis[1],axis[0]) transposition
    4645             :     */
    4646        1005 :     const size_t nDims(GetDimensionCount());
    4647        1005 :     size_t nCurStrideForRowMajorStrides = 1;
    4648        1005 :     bool bRowMajorStrides = true;
    4649        1005 :     size_t nElts = 1;
    4650        1005 :     size_t nLastIdx = 0;
    4651        2669 :     for (size_t i = nDims; i > 0;)
    4652             :     {
    4653        1664 :         --i;
    4654        1664 :         if (bufferStride[i] < 0)
    4655           0 :             return false;
    4656        1664 :         if (static_cast<size_t>(bufferStride[i]) !=
    4657             :             nCurStrideForRowMajorStrides)
    4658             :         {
    4659         278 :             bRowMajorStrides = false;
    4660             :         }
    4661             :         // Integer overflows have already been checked in CheckReadWriteParams()
    4662        1664 :         nCurStrideForRowMajorStrides *= count[i];
    4663        1664 :         nElts *= count[i];
    4664        1664 :         nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
    4665             :     }
    4666        1005 :     if (bRowMajorStrides)
    4667         801 :         return false;
    4668         204 :     return nLastIdx == nElts - 1;
    4669             : }
    4670             : 
    4671             : /************************************************************************/
    4672             : /*                   CopyToFinalBufferSameDataType()                    */
    4673             : /************************************************************************/
    4674             : 
    4675             : template <size_t N>
    4676          72 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
    4677             :                                    size_t nDims, const size_t *count,
    4678             :                                    const GPtrDiff_t *bufferStride)
    4679             : {
    4680         144 :     std::vector<size_t> anStackCount(nDims);
    4681         144 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    4682          72 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
    4683             : #if defined(__GNUC__)
    4684             : #pragma GCC diagnostic push
    4685             : #pragma GCC diagnostic ignored "-Wnull-dereference"
    4686             : #endif
    4687          72 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    4688             : #if defined(__GNUC__)
    4689             : #pragma GCC diagnostic pop
    4690             : #endif
    4691          72 :     size_t iDim = 0;
    4692             : 
    4693         761 : lbl_next_depth:
    4694         761 :     if (iDim == nDims - 1)
    4695             :     {
    4696         673 :         size_t n = count[iDim];
    4697         673 :         GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
    4698         673 :         const auto bufferStrideLastDim = bufferStride[iDim] * N;
    4699       29210 :         while (n > 0)
    4700             :         {
    4701       28537 :             --n;
    4702       28537 :             memcpy(pabyDstBuffer, pabySrcBuffer, N);
    4703       28537 :             pabyDstBuffer += bufferStrideLastDim;
    4704       28537 :             pabySrcBuffer += N;
    4705             :         }
    4706             :     }
    4707             :     else
    4708             :     {
    4709          88 :         anStackCount[iDim] = count[iDim];
    4710             :         while (true)
    4711             :         {
    4712         689 :             ++iDim;
    4713         689 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    4714         689 :             goto lbl_next_depth;
    4715         689 :         lbl_return_to_caller_in_loop:
    4716         689 :             --iDim;
    4717         689 :             --anStackCount[iDim];
    4718         689 :             if (anStackCount[iDim] == 0)
    4719          88 :                 break;
    4720         601 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
    4721             :         }
    4722             :     }
    4723         761 :     if (iDim > 0)
    4724         689 :         goto lbl_return_to_caller_in_loop;
    4725          72 : }
    4726             : 
    4727             : /************************************************************************/
    4728             : /*                        CopyToFinalBuffer()                           */
    4729             : /************************************************************************/
    4730             : 
    4731         183 : static void CopyToFinalBuffer(const void *pSrcBuffer,
    4732             :                               const GDALExtendedDataType &eSrcDataType,
    4733             :                               void *pDstBuffer,
    4734             :                               const GDALExtendedDataType &eDstDataType,
    4735             :                               size_t nDims, const size_t *count,
    4736             :                               const GPtrDiff_t *bufferStride)
    4737             : {
    4738         183 :     const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
    4739             :     // Use specialized implementation for well-known data types when no
    4740             :     // type conversion is needed
    4741         183 :     if (eSrcDataType == eDstDataType)
    4742             :     {
    4743         122 :         if (nSrcDataTypeSize == 1)
    4744             :         {
    4745          41 :             CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
    4746             :                                              count, bufferStride);
    4747          72 :             return;
    4748             :         }
    4749          81 :         else if (nSrcDataTypeSize == 2)
    4750             :         {
    4751           1 :             CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
    4752             :                                              count, bufferStride);
    4753           1 :             return;
    4754             :         }
    4755          80 :         else if (nSrcDataTypeSize == 4)
    4756             :         {
    4757          22 :             CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
    4758             :                                              count, bufferStride);
    4759          22 :             return;
    4760             :         }
    4761          58 :         else if (nSrcDataTypeSize == 8)
    4762             :         {
    4763           8 :             CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
    4764             :                                              count, bufferStride);
    4765           8 :             return;
    4766             :         }
    4767             :     }
    4768             : 
    4769         111 :     const size_t nDstDataTypeSize(eDstDataType.GetSize());
    4770         222 :     std::vector<size_t> anStackCount(nDims);
    4771         222 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    4772         111 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
    4773         111 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    4774         111 :     size_t iDim = 0;
    4775             : 
    4776         380 : lbl_next_depth:
    4777         380 :     if (iDim == nDims - 1)
    4778             :     {
    4779         370 :         GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
    4780         370 :                                          pabyDstBufferStack[iDim], eDstDataType,
    4781         370 :                                          bufferStride[iDim], count[iDim]);
    4782         370 :         pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
    4783             :     }
    4784             :     else
    4785             :     {
    4786          10 :         anStackCount[iDim] = count[iDim];
    4787             :         while (true)
    4788             :         {
    4789         269 :             ++iDim;
    4790         269 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    4791         269 :             goto lbl_next_depth;
    4792         269 :         lbl_return_to_caller_in_loop:
    4793         269 :             --iDim;
    4794         269 :             --anStackCount[iDim];
    4795         269 :             if (anStackCount[iDim] == 0)
    4796          10 :                 break;
    4797         259 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
    4798             :         }
    4799             :     }
    4800         380 :     if (iDim > 0)
    4801         269 :         goto lbl_return_to_caller_in_loop;
    4802             : }
    4803             : 
    4804             : /************************************************************************/
    4805             : /*                      TransposeLast2Dims()                            */
    4806             : /************************************************************************/
    4807             : 
    4808          19 : static bool TransposeLast2Dims(void *pDstBuffer,
    4809             :                                const GDALExtendedDataType &eDT,
    4810             :                                const size_t nDims, const size_t *count,
    4811             :                                const size_t nEltsNonLast2Dims)
    4812             : {
    4813          19 :     const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
    4814          19 :     const auto nDTSize = eDT.GetSize();
    4815             :     void *pTempBufferForLast2DimsTranspose =
    4816          19 :         VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
    4817          19 :     if (pTempBufferForLast2DimsTranspose == nullptr)
    4818           0 :         return false;
    4819             : 
    4820          19 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
    4821          58 :     for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
    4822             :     {
    4823          39 :         GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
    4824             :                         pTempBufferForLast2DimsTranspose,
    4825          39 :                         eDT.GetNumericDataType(), count[nDims - 1],
    4826          39 :                         count[nDims - 2]);
    4827          39 :         memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
    4828             :                nDTSize * nEltsLast2Dims);
    4829          39 :         pabyDstBuffer += nDTSize * nEltsLast2Dims;
    4830             :     }
    4831             : 
    4832          19 :     VSIFree(pTempBufferForLast2DimsTranspose);
    4833             : 
    4834          19 :     return true;
    4835             : }
    4836             : 
    4837             : /************************************************************************/
    4838             : /*                      ReadForTransposedRequest()                      */
    4839             : /************************************************************************/
    4840             : 
    4841             : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
    4842             : // transposed view yield to extremely poor/unusable performance. This fixes
    4843             : // this by using temporary memory to read in a contiguous buffer in a
    4844             : // row-major order, and then do the transposition to the final buffer.
    4845             : 
    4846         202 : bool GDALMDArray::ReadForTransposedRequest(
    4847             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    4848             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
    4849             :     void *pDstBuffer) const
    4850             : {
    4851         202 :     const size_t nDims(GetDimensionCount());
    4852         202 :     if (nDims == 0)
    4853             :     {
    4854           0 :         CPLAssert(false);
    4855             :         return false;  // shouldn't happen
    4856             :     }
    4857         202 :     size_t nElts = 1;
    4858         526 :     for (size_t i = 0; i < nDims; ++i)
    4859         324 :         nElts *= count[i];
    4860             : 
    4861         404 :     std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
    4862         202 :     tmpBufferStrides.back() = 1;
    4863         324 :     for (size_t i = nDims - 1; i > 0;)
    4864             :     {
    4865         122 :         --i;
    4866         122 :         tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
    4867             :     }
    4868             : 
    4869         202 :     const auto &eDT = GetDataType();
    4870         202 :     const auto nDTSize = eDT.GetSize();
    4871         343 :     if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
    4872         359 :         static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
    4873          16 :         (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
    4874             :     {
    4875             :         // Optimization of the optimization if only the last 2 dims are
    4876             :         // transposed that saves on temporary buffer allocation
    4877          23 :         const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
    4878          23 :         size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
    4879          23 :         bool bRowMajorStridesForNonLast2Dims = true;
    4880          23 :         size_t nEltsNonLast2Dims = 1;
    4881          40 :         for (size_t i = nDims - 2; i > 0;)
    4882             :         {
    4883          17 :             --i;
    4884          17 :             if (static_cast<size_t>(bufferStride[i]) !=
    4885             :                 nCurStrideForRowMajorStrides)
    4886             :             {
    4887           4 :                 bRowMajorStridesForNonLast2Dims = false;
    4888             :             }
    4889             :             // Integer overflows have already been checked in
    4890             :             // CheckReadWriteParams()
    4891          17 :             nCurStrideForRowMajorStrides *= count[i];
    4892          17 :             nEltsNonLast2Dims *= count[i];
    4893             :         }
    4894          23 :         if (bRowMajorStridesForNonLast2Dims)
    4895             :         {
    4896             :             // We read in the final buffer!
    4897          19 :             if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
    4898          19 :                        eDT, pDstBuffer))
    4899             :             {
    4900           0 :                 return false;
    4901             :             }
    4902             : 
    4903          19 :             return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
    4904          19 :                                       nEltsNonLast2Dims);
    4905             :         }
    4906             :     }
    4907             : 
    4908         183 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
    4909         183 :     if (pTempBuffer == nullptr)
    4910           0 :         return false;
    4911             : 
    4912         183 :     if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
    4913         183 :                pTempBuffer))
    4914             :     {
    4915           0 :         VSIFree(pTempBuffer);
    4916           0 :         return false;
    4917             :     }
    4918         183 :     CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
    4919             :                       count, bufferStride);
    4920             : 
    4921         183 :     if (eDT.NeedsFreeDynamicMemory())
    4922             :     {
    4923         100 :         GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
    4924         200 :         for (size_t i = 0; i < nElts; ++i)
    4925             :         {
    4926         100 :             eDT.FreeDynamicMemory(pabyPtr);
    4927         100 :             pabyPtr += nDTSize;
    4928             :         }
    4929             :     }
    4930             : 
    4931         183 :     VSIFree(pTempBuffer);
    4932         183 :     return true;
    4933             : }
    4934             : 
    4935             : /************************************************************************/
    4936             : /*               IsStepOneContiguousRowMajorOrderedSameDataType()       */
    4937             : /************************************************************************/
    4938             : 
    4939             : // Returns true if at all following conditions are met:
    4940             : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
    4941             : // defines a row-major ordered contiguous buffer.
    4942          78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
    4943             :     const size_t *count, const GInt64 *arrayStep,
    4944             :     const GPtrDiff_t *bufferStride,
    4945             :     const GDALExtendedDataType &bufferDataType) const
    4946             : {
    4947          78 :     if (bufferDataType != GetDataType())
    4948           5 :         return false;
    4949          73 :     size_t nExpectedStride = 1;
    4950         166 :     for (size_t i = GetDimensionCount(); i > 0;)
    4951             :     {
    4952          96 :         --i;
    4953          96 :         if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
    4954          94 :             static_cast<size_t>(bufferStride[i]) != nExpectedStride)
    4955             :         {
    4956           3 :             return false;
    4957             :         }
    4958          93 :         nExpectedStride *= count[i];
    4959             :     }
    4960          70 :     return true;
    4961             : }
    4962             : 
    4963             : /************************************************************************/
    4964             : /*                      ReadUsingContiguousIRead()                      */
    4965             : /************************************************************************/
    4966             : 
    4967             : // Used for example by the TileDB driver when requesting it with
    4968             : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
    4969             : // not defining a row-major ordered contiguous buffer.
    4970             : // Should only be called when at least one of the above conditions are true,
    4971             : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
    4972             : // returning none.
    4973             : // This method will call IRead() again with arrayStep[] == 1,
    4974             : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
    4975             : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
    4976             : // content of that temporary buffer onto pDstBuffer.
    4977           7 : bool GDALMDArray::ReadUsingContiguousIRead(
    4978             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    4979             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
    4980             :     void *pDstBuffer) const
    4981             : {
    4982           7 :     const size_t nDims(GetDimensionCount());
    4983          14 :     std::vector<GUInt64> anTmpStartIdx(nDims);
    4984          14 :     std::vector<size_t> anTmpCount(nDims);
    4985           7 :     const auto &oType = GetDataType();
    4986           7 :     size_t nMemArraySize = oType.GetSize();
    4987          14 :     std::vector<GPtrDiff_t> anTmpStride(nDims);
    4988           7 :     GPtrDiff_t nStride = 1;
    4989          18 :     for (size_t i = nDims; i > 0;)
    4990             :     {
    4991          11 :         --i;
    4992          11 :         if (arrayStep[i] > 0)
    4993           9 :             anTmpStartIdx[i] = arrayStartIdx[i];
    4994             :         else
    4995           2 :             anTmpStartIdx[i] =
    4996           2 :                 arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
    4997             :         const uint64_t nCount =
    4998          11 :             (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
    4999          11 :         if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
    5000             :         {
    5001           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    5002             :                      "Read() failed due to too large memory requirement");
    5003           0 :             return false;
    5004             :         }
    5005          11 :         anTmpCount[i] = static_cast<size_t>(nCount);
    5006          11 :         nMemArraySize *= anTmpCount[i];
    5007          11 :         anTmpStride[i] = nStride;
    5008          11 :         nStride *= anTmpCount[i];
    5009             :     }
    5010             :     std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
    5011          14 :         VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
    5012           7 :     if (!pTmpBuffer)
    5013           0 :         return false;
    5014           7 :     if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
    5015          14 :                std::vector<GInt64>(nDims, 1).data(),  // steps
    5016           7 :                anTmpStride.data(), oType, pTmpBuffer.get()))
    5017             :     {
    5018           0 :         return false;
    5019             :     }
    5020          14 :     std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
    5021          18 :     for (size_t i = 0; i < nDims; ++i)
    5022             :     {
    5023          11 :         if (arrayStep[i] > 0)
    5024           9 :             anTmpStartIdx[i] = 0;
    5025             :         else
    5026           2 :             anTmpStartIdx[i] = anTmpCount[i] - 1;
    5027          22 :         apoTmpDims[i] = std::make_shared<GDALDimension>(
    5028          22 :             std::string(), std::string(), std::string(), std::string(),
    5029          22 :             anTmpCount[i]);
    5030             :     }
    5031             :     auto poMEMArray =
    5032          14 :         MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
    5033          21 :     return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
    5034           7 :            poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
    5035           7 :                             bufferStride, bufferDataType, pDstBuffer);
    5036             : }
    5037             : 
    5038             : //! @endcond
    5039             : 
    5040             : /************************************************************************/
    5041             : /*                       GDALSlicedMDArray                              */
    5042             : /************************************************************************/
    5043             : 
    5044             : class GDALSlicedMDArray final : public GDALPamMDArray
    5045             : {
    5046             :   private:
    5047             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5048             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    5049             :     std::vector<size_t> m_mapDimIdxToParentDimIdx{};  // of size m_dims.size()
    5050             :     std::vector<std::shared_ptr<GDALMDArray>> m_apoNewIndexingVariables{};
    5051             :     std::vector<Range>
    5052             :         m_parentRanges{};  // of size m_poParent->GetDimensionCount()
    5053             : 
    5054             :     mutable std::vector<GUInt64> m_parentStart;
    5055             :     mutable std::vector<size_t> m_parentCount;
    5056             :     mutable std::vector<GInt64> m_parentStep;
    5057             :     mutable std::vector<GPtrDiff_t> m_parentStride;
    5058             : 
    5059             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
    5060             :                              const GInt64 *arrayStep,
    5061             :                              const GPtrDiff_t *bufferStride) const;
    5062             : 
    5063             :   protected:
    5064         690 :     explicit GDALSlicedMDArray(
    5065             :         const std::shared_ptr<GDALMDArray> &poParent,
    5066             :         const std::string &viewExpr,
    5067             :         std::vector<std::shared_ptr<GDALDimension>> &&dims,
    5068             :         std::vector<size_t> &&mapDimIdxToParentDimIdx,
    5069             :         std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
    5070             :         std::vector<Range> &&parentRanges)
    5071        2070 :         : GDALAbstractMDArray(std::string(), "Sliced view of " +
    5072        2070 :                                                  poParent->GetFullName() +
    5073        1380 :                                                  " (" + viewExpr + ")"),
    5074        1380 :           GDALPamMDArray(std::string(),
    5075        1380 :                          "Sliced view of " + poParent->GetFullName() + " (" +
    5076        1380 :                              viewExpr + ")",
    5077        1380 :                          GDALPamMultiDim::GetPAM(poParent),
    5078             :                          poParent->GetContext()),
    5079        1380 :           m_poParent(std::move(poParent)), m_dims(std::move(dims)),
    5080         690 :           m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
    5081         690 :           m_apoNewIndexingVariables(std::move(apoNewIndexingVariables)),
    5082         690 :           m_parentRanges(std::move(parentRanges)),
    5083         690 :           m_parentStart(m_poParent->GetDimensionCount()),
    5084         690 :           m_parentCount(m_poParent->GetDimensionCount(), 1),
    5085         690 :           m_parentStep(m_poParent->GetDimensionCount()),
    5086        5520 :           m_parentStride(m_poParent->GetDimensionCount())
    5087             :     {
    5088         690 :     }
    5089             : 
    5090             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5091             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5092             :                const GDALExtendedDataType &bufferDataType,
    5093             :                void *pDstBuffer) const override;
    5094             : 
    5095             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    5096             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5097             :                 const GDALExtendedDataType &bufferDataType,
    5098             :                 const void *pSrcBuffer) override;
    5099             : 
    5100             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5101             :                      CSLConstList papszOptions) const override;
    5102             : 
    5103             :   public:
    5104             :     static std::shared_ptr<GDALSlicedMDArray>
    5105         690 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5106             :            const std::string &viewExpr,
    5107             :            std::vector<std::shared_ptr<GDALDimension>> &&dims,
    5108             :            std::vector<size_t> &&mapDimIdxToParentDimIdx,
    5109             :            std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
    5110             :            std::vector<Range> &&parentRanges)
    5111             :     {
    5112         690 :         CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
    5113         690 :         CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
    5114             : 
    5115             :         auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
    5116         690 :             poParent, viewExpr, std::move(dims),
    5117         690 :             std::move(mapDimIdxToParentDimIdx),
    5118         690 :             std::move(apoNewIndexingVariables), std::move(parentRanges))));
    5119         690 :         newAr->SetSelf(newAr);
    5120         690 :         return newAr;
    5121             :     }
    5122             : 
    5123          77 :     bool IsWritable() const override
    5124             :     {
    5125          77 :         return m_poParent->IsWritable();
    5126             :     }
    5127             : 
    5128        1123 :     const std::string &GetFilename() const override
    5129             :     {
    5130        1123 :         return m_poParent->GetFilename();
    5131             :     }
    5132             : 
    5133             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5134        4152 :     GetDimensions() const override
    5135             :     {
    5136        4152 :         return m_dims;
    5137             :     }
    5138             : 
    5139        1593 :     const GDALExtendedDataType &GetDataType() const override
    5140             :     {
    5141        1593 :         return m_poParent->GetDataType();
    5142             :     }
    5143             : 
    5144           2 :     const std::string &GetUnit() const override
    5145             :     {
    5146           2 :         return m_poParent->GetUnit();
    5147             :     }
    5148             : 
    5149             :     // bool SetUnit(const std::string& osUnit) override  { return
    5150             :     // m_poParent->SetUnit(osUnit); }
    5151             : 
    5152           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5153             :     {
    5154           4 :         auto poSrcSRS = m_poParent->GetSpatialRef();
    5155           2 :         if (!poSrcSRS)
    5156           1 :             return nullptr;
    5157           2 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
    5158           2 :         std::vector<int> dstMapping;
    5159           3 :         for (int srcAxis : srcMapping)
    5160             :         {
    5161           2 :             bool bFound = false;
    5162           3 :             for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
    5163             :             {
    5164           3 :                 if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
    5165           3 :                     srcAxis - 1)
    5166             :                 {
    5167           2 :                     dstMapping.push_back(static_cast<int>(i) + 1);
    5168           2 :                     bFound = true;
    5169           2 :                     break;
    5170             :                 }
    5171             :             }
    5172           2 :             if (!bFound)
    5173             :             {
    5174           0 :                 dstMapping.push_back(0);
    5175             :             }
    5176             :         }
    5177           2 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
    5178           1 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
    5179           1 :         return poClone;
    5180             :     }
    5181             : 
    5182          61 :     const void *GetRawNoDataValue() const override
    5183             :     {
    5184          61 :         return m_poParent->GetRawNoDataValue();
    5185             :     }
    5186             : 
    5187             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
    5188             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
    5189             : 
    5190           2 :     double GetOffset(bool *pbHasOffset,
    5191             :                      GDALDataType *peStorageType) const override
    5192             :     {
    5193           2 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5194             :     }
    5195             : 
    5196           2 :     double GetScale(bool *pbHasScale,
    5197             :                     GDALDataType *peStorageType) const override
    5198             :     {
    5199           2 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5200             :     }
    5201             : 
    5202             :     // bool SetOffset(double dfOffset) override { return
    5203             :     // m_poParent->SetOffset(dfOffset); }
    5204             : 
    5205             :     // bool SetScale(double dfScale) override { return
    5206             :     // m_poParent->SetScale(dfScale); }
    5207             : 
    5208         263 :     std::vector<GUInt64> GetBlockSize() const override
    5209             :     {
    5210         263 :         std::vector<GUInt64> ret(GetDimensionCount());
    5211         526 :         const auto parentBlockSize(m_poParent->GetBlockSize());
    5212         749 :         for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
    5213             :         {
    5214         486 :             const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
    5215         486 :             if (iOldAxis != static_cast<size_t>(-1))
    5216             :             {
    5217         486 :                 ret[i] = parentBlockSize[iOldAxis];
    5218             :             }
    5219             :         }
    5220         526 :         return ret;
    5221             :     }
    5222             : 
    5223             :     std::shared_ptr<GDALAttribute>
    5224           6 :     GetAttribute(const std::string &osName) const override
    5225             :     {
    5226           6 :         return m_poParent->GetAttribute(osName);
    5227             :     }
    5228             : 
    5229             :     std::vector<std::shared_ptr<GDALAttribute>>
    5230          35 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    5231             :     {
    5232          35 :         return m_poParent->GetAttributes(papszOptions);
    5233             :     }
    5234             : };
    5235             : 
    5236             : /************************************************************************/
    5237             : /*                        PrepareParentArrays()                         */
    5238             : /************************************************************************/
    5239             : 
    5240         553 : void GDALSlicedMDArray::PrepareParentArrays(
    5241             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    5242             :     const GPtrDiff_t *bufferStride) const
    5243             : {
    5244         553 :     const size_t nParentDimCount = m_parentRanges.size();
    5245        1663 :     for (size_t i = 0; i < nParentDimCount; i++)
    5246             :     {
    5247             :         // For dimensions in parent that have no existence in sliced array
    5248        1110 :         m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
    5249             :     }
    5250             : 
    5251        1431 :     for (size_t i = 0; i < m_dims.size(); i++)
    5252             :     {
    5253         878 :         const auto iParent = m_mapDimIdxToParentDimIdx[i];
    5254         878 :         if (iParent != static_cast<size_t>(-1))
    5255             :         {
    5256         876 :             m_parentStart[iParent] =
    5257         876 :                 m_parentRanges[iParent].m_nIncr >= 0
    5258         876 :                     ? m_parentRanges[iParent].m_nStartIdx +
    5259         787 :                           arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
    5260          89 :                     : m_parentRanges[iParent].m_nStartIdx -
    5261         178 :                           arrayStartIdx[i] *
    5262          89 :                               static_cast<GUInt64>(
    5263          89 :                                   -m_parentRanges[iParent].m_nIncr);
    5264         876 :             m_parentCount[iParent] = count[i];
    5265         876 :             if (arrayStep)
    5266             :             {
    5267         875 :                 m_parentStep[iParent] =
    5268         875 :                     count[i] == 1 ? 1 :
    5269             :                                   // other checks should have ensured this does
    5270             :                         // not overflow
    5271         681 :                         arrayStep[i] * m_parentRanges[iParent].m_nIncr;
    5272             :             }
    5273         876 :             if (bufferStride)
    5274             :             {
    5275         875 :                 m_parentStride[iParent] = bufferStride[i];
    5276             :             }
    5277             :         }
    5278             :     }
    5279         553 : }
    5280             : 
    5281             : /************************************************************************/
    5282             : /*                             IRead()                                  */
    5283             : /************************************************************************/
    5284             : 
    5285         520 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5286             :                               const GInt64 *arrayStep,
    5287             :                               const GPtrDiff_t *bufferStride,
    5288             :                               const GDALExtendedDataType &bufferDataType,
    5289             :                               void *pDstBuffer) const
    5290             : {
    5291         520 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5292        1040 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
    5293         520 :                             m_parentStep.data(), m_parentStride.data(),
    5294         520 :                             bufferDataType, pDstBuffer);
    5295             : }
    5296             : 
    5297             : /************************************************************************/
    5298             : /*                             IWrite()                                  */
    5299             : /************************************************************************/
    5300             : 
    5301          32 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
    5302             :                                const size_t *count, const GInt64 *arrayStep,
    5303             :                                const GPtrDiff_t *bufferStride,
    5304             :                                const GDALExtendedDataType &bufferDataType,
    5305             :                                const void *pSrcBuffer)
    5306             : {
    5307          32 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5308          64 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
    5309          32 :                              m_parentStep.data(), m_parentStride.data(),
    5310          32 :                              bufferDataType, pSrcBuffer);
    5311             : }
    5312             : 
    5313             : /************************************************************************/
    5314             : /*                             IAdviseRead()                            */
    5315             : /************************************************************************/
    5316             : 
    5317           1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
    5318             :                                     const size_t *count,
    5319             :                                     CSLConstList papszOptions) const
    5320             : {
    5321           1 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
    5322           1 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
    5323           1 :                                   papszOptions);
    5324             : }
    5325             : 
    5326             : /************************************************************************/
    5327             : /*                        CreateSlicedArray()                           */
    5328             : /************************************************************************/
    5329             : 
    5330             : static std::shared_ptr<GDALMDArray>
    5331         626 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
    5332             :                   const std::string &viewExpr, const std::string &activeSlice,
    5333             :                   bool bRenameDimensions,
    5334             :                   std::vector<GDALMDArray::ViewSpec> &viewSpecs)
    5335             : {
    5336         626 :     const auto &srcDims(self->GetDimensions());
    5337         626 :     if (srcDims.empty())
    5338             :     {
    5339           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
    5340           2 :         return nullptr;
    5341             :     }
    5342             : 
    5343        1248 :     CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
    5344         624 :     const auto nTokens = static_cast<size_t>(aosTokens.size());
    5345             : 
    5346        1248 :     std::vector<std::shared_ptr<GDALDimension>> newDims;
    5347        1248 :     std::vector<size_t> mapDimIdxToParentDimIdx;
    5348        1248 :     std::vector<GDALSlicedMDArray::Range> parentRanges;
    5349         624 :     newDims.reserve(nTokens);
    5350         624 :     mapDimIdxToParentDimIdx.reserve(nTokens);
    5351         624 :     parentRanges.reserve(nTokens);
    5352             : 
    5353         624 :     bool bGotEllipsis = false;
    5354         624 :     size_t nCurSrcDim = 0;
    5355        1248 :     std::vector<std::shared_ptr<GDALMDArray>> apoNewIndexingVariables;
    5356        1842 :     for (size_t i = 0; i < nTokens; i++)
    5357             :     {
    5358        1235 :         const char *pszIdxSpec = aosTokens[i];
    5359        1235 :         if (EQUAL(pszIdxSpec, "..."))
    5360             :         {
    5361          46 :             if (bGotEllipsis)
    5362             :             {
    5363           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5364             :                          "Only one single ellipsis is supported");
    5365           2 :                 return nullptr;
    5366             :             }
    5367          44 :             bGotEllipsis = true;
    5368          44 :             const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
    5369          97 :             for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
    5370             :             {
    5371          53 :                 parentRanges.emplace_back(0, 1);
    5372          53 :                 newDims.push_back(srcDims[nCurSrcDim]);
    5373          53 :                 mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5374             :             }
    5375          44 :             continue;
    5376             :         }
    5377        1189 :         else if (EQUAL(pszIdxSpec, "newaxis") ||
    5378        1186 :                  EQUAL(pszIdxSpec, "np.newaxis"))
    5379             :         {
    5380           3 :             newDims.push_back(std::make_shared<GDALDimension>(
    5381           6 :                 std::string(), "newaxis", std::string(), std::string(), 1));
    5382           3 :             mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
    5383           3 :             continue;
    5384             :         }
    5385        1186 :         else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
    5386             :         {
    5387         327 :             if (nCurSrcDim >= srcDims.size())
    5388             :             {
    5389           2 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
    5390             :                          activeSlice.c_str());
    5391           7 :                 return nullptr;
    5392             :             }
    5393             : 
    5394         325 :             auto nVal = CPLAtoGIntBig(pszIdxSpec);
    5395         325 :             GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
    5396         325 :             if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
    5397         321 :                 (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
    5398             :             {
    5399           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5400             :                          "Index " CPL_FRMT_GIB " is out of bounds", nVal);
    5401           5 :                 return nullptr;
    5402             :             }
    5403         320 :             if (nVal < 0)
    5404           0 :                 nVal += nDimSize;
    5405         320 :             parentRanges.emplace_back(nVal, 0);
    5406             :         }
    5407             :         else
    5408             :         {
    5409         859 :             if (nCurSrcDim >= srcDims.size())
    5410             :             {
    5411           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
    5412             :                          activeSlice.c_str());
    5413           8 :                 return nullptr;
    5414             :             }
    5415             : 
    5416             :             CPLStringList aosRangeTokens(
    5417         858 :                 CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
    5418         858 :             int nRangeTokens = aosRangeTokens.size();
    5419         858 :             if (nRangeTokens > 3)
    5420             :             {
    5421           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
    5422             :                          pszIdxSpec);
    5423           1 :                 return nullptr;
    5424             :             }
    5425         857 :             if (nRangeTokens <= 1)
    5426             :             {
    5427           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
    5428             :                          pszIdxSpec);
    5429           1 :                 return nullptr;
    5430             :             }
    5431         856 :             const char *pszStart = aosRangeTokens[0];
    5432         856 :             const char *pszEnd = aosRangeTokens[1];
    5433         856 :             const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
    5434         856 :             GDALSlicedMDArray::Range range;
    5435         856 :             const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
    5436         856 :             range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
    5437        1711 :             if (range.m_nIncr == 0 ||
    5438         855 :                 range.m_nIncr == std::numeric_limits<GInt64>::min())
    5439             :             {
    5440           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
    5441           1 :                 return nullptr;
    5442             :             }
    5443         855 :             auto startIdx(CPLAtoGIntBig(pszStart));
    5444         855 :             if (startIdx < 0)
    5445             :             {
    5446           0 :                 if (nDimSize < static_cast<GUInt64>(-startIdx))
    5447           0 :                     startIdx = 0;
    5448             :                 else
    5449           0 :                     startIdx = nDimSize + startIdx;
    5450             :             }
    5451         855 :             const bool bPosIncr = range.m_nIncr > 0;
    5452         855 :             range.m_nStartIdx = startIdx;
    5453        1710 :             range.m_nStartIdx = EQUAL(pszStart, "")
    5454         855 :                                     ? (bPosIncr ? 0 : nDimSize - 1)
    5455             :                                     : range.m_nStartIdx;
    5456         855 :             if (range.m_nStartIdx >= nDimSize - 1)
    5457         203 :                 range.m_nStartIdx = nDimSize - 1;
    5458         855 :             auto endIdx(CPLAtoGIntBig(pszEnd));
    5459         855 :             if (endIdx < 0)
    5460             :             {
    5461           1 :                 const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
    5462           1 :                 if (nDimSize < positiveEndIdx)
    5463           0 :                     endIdx = 0;
    5464             :                 else
    5465           1 :                     endIdx = nDimSize - positiveEndIdx;
    5466             :             }
    5467         855 :             GUInt64 nEndIdx = endIdx;
    5468         855 :             nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
    5469         855 :             if (pszStart[0] || pszEnd[0])
    5470             :             {
    5471         636 :                 if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
    5472         633 :                     (!bPosIncr && range.m_nStartIdx <= nEndIdx))
    5473             :                 {
    5474           4 :                     CPLError(CE_Failure, CPLE_AppDefined,
    5475             :                              "Output dimension of size 0 is not allowed");
    5476           4 :                     return nullptr;
    5477             :                 }
    5478             :             }
    5479         851 :             int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
    5480         851 :             const auto nAbsIncr = std::abs(range.m_nIncr);
    5481         851 :             const GUInt64 newSize =
    5482         219 :                 (pszStart[0] == 0 && pszEnd[0] == 0 &&
    5483         219 :                  range.m_nStartIdx == nEndIdx)
    5484        1702 :                     ? 1
    5485             :                 : bPosIncr
    5486         895 :                     ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
    5487          45 :                     : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
    5488         851 :             const auto &poSrcDim = srcDims[nCurSrcDim];
    5489        1401 :             if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
    5490         550 :                 newSize == poSrcDim->GetSize())
    5491             :             {
    5492         181 :                 newDims.push_back(poSrcDim);
    5493             :             }
    5494             :             else
    5495             :             {
    5496        1340 :                 std::string osNewDimName(poSrcDim->GetName());
    5497         670 :                 if (bRenameDimensions)
    5498             :                 {
    5499             :                     osNewDimName =
    5500        1244 :                         "subset_" + poSrcDim->GetName() +
    5501             :                         CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
    5502             :                                    "_" CPL_FRMT_GUIB,
    5503         622 :                                    static_cast<GUIntBig>(range.m_nStartIdx),
    5504         622 :                                    static_cast<GIntBig>(range.m_nIncr),
    5505         622 :                                    static_cast<GUIntBig>(newSize));
    5506             :                 }
    5507             :                 auto poNewDim = std::make_shared<GDALDimensionWeakIndexingVar>(
    5508        1340 :                     std::string(), osNewDimName, poSrcDim->GetType(),
    5509         670 :                     range.m_nIncr > 0 ? poSrcDim->GetDirection()
    5510             :                                       : std::string(),
    5511        1340 :                     newSize);
    5512         670 :                 auto poSrcIndexingVar = poSrcDim->GetIndexingVariable();
    5513         753 :                 if (poSrcIndexingVar &&
    5514         753 :                     poSrcIndexingVar->GetDimensionCount() == 1 &&
    5515          83 :                     poSrcIndexingVar->GetDimensions()[0] == poSrcDim)
    5516             :                 {
    5517             :                     std::vector<std::shared_ptr<GDALDimension>>
    5518         332 :                         indexingVarNewDims{poNewDim};
    5519         166 :                     std::vector<size_t> indexingVarMapDimIdxToParentDimIdx{0};
    5520             :                     std::vector<std::shared_ptr<GDALMDArray>>
    5521         166 :                         indexingVarNewIndexingVar;
    5522             :                     std::vector<GDALSlicedMDArray::Range>
    5523         166 :                         indexingVarParentRanges{range};
    5524             :                     auto poNewIndexingVar = GDALSlicedMDArray::Create(
    5525             :                         poSrcIndexingVar, pszIdxSpec,
    5526          83 :                         std::move(indexingVarNewDims),
    5527          83 :                         std::move(indexingVarMapDimIdxToParentDimIdx),
    5528          83 :                         std::move(indexingVarNewIndexingVar),
    5529         249 :                         std::move(indexingVarParentRanges));
    5530          83 :                     poNewDim->SetIndexingVariable(poNewIndexingVar);
    5531          83 :                     apoNewIndexingVariables.push_back(
    5532          83 :                         std::move(poNewIndexingVar));
    5533             :                 }
    5534         670 :                 newDims.push_back(std::move(poNewDim));
    5535             :             }
    5536         851 :             mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5537         851 :             parentRanges.emplace_back(range);
    5538             :         }
    5539             : 
    5540        1171 :         nCurSrcDim++;
    5541             :     }
    5542         680 :     for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
    5543             :     {
    5544          73 :         parentRanges.emplace_back(0, 1);
    5545          73 :         newDims.push_back(srcDims[nCurSrcDim]);
    5546          73 :         mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5547             :     }
    5548             : 
    5549         607 :     GDALMDArray::ViewSpec viewSpec;
    5550         607 :     viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
    5551         607 :     viewSpec.m_parentRanges = parentRanges;
    5552         607 :     viewSpecs.emplace_back(std::move(viewSpec));
    5553             : 
    5554        1214 :     return GDALSlicedMDArray::Create(
    5555         607 :         self, viewExpr, std::move(newDims), std::move(mapDimIdxToParentDimIdx),
    5556        1214 :         std::move(apoNewIndexingVariables), std::move(parentRanges));
    5557             : }
    5558             : 
    5559             : /************************************************************************/
    5560             : /*                       GDALExtractFieldMDArray                        */
    5561             : /************************************************************************/
    5562             : 
    5563             : class GDALExtractFieldMDArray final : public GDALPamMDArray
    5564             : {
    5565             :   private:
    5566             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5567             :     GDALExtendedDataType m_dt;
    5568             :     std::string m_srcCompName;
    5569             :     mutable std::vector<GByte> m_pabyNoData{};
    5570             : 
    5571             :   protected:
    5572          98 :     GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
    5573             :                             const std::string &fieldName,
    5574             :                             const std::unique_ptr<GDALEDTComponent> &srcComp)
    5575         392 :         : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
    5576         196 :                                                  " of " +
    5577          98 :                                                  poParent->GetFullName()),
    5578             :           GDALPamMDArray(
    5579         196 :               std::string(),
    5580         196 :               "Extract field " + fieldName + " of " + poParent->GetFullName(),
    5581         196 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
    5582             :           m_poParent(poParent), m_dt(srcComp->GetType()),
    5583         490 :           m_srcCompName(srcComp->GetName())
    5584             :     {
    5585          98 :         m_pabyNoData.resize(m_dt.GetSize());
    5586          98 :     }
    5587             : 
    5588             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5589             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5590             :                const GDALExtendedDataType &bufferDataType,
    5591             :                void *pDstBuffer) const override;
    5592             : 
    5593           1 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5594             :                      CSLConstList papszOptions) const override
    5595             :     {
    5596           1 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
    5597             :     }
    5598             : 
    5599             :   public:
    5600             :     static std::shared_ptr<GDALExtractFieldMDArray>
    5601          98 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5602             :            const std::string &fieldName,
    5603             :            const std::unique_ptr<GDALEDTComponent> &srcComp)
    5604             :     {
    5605             :         auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
    5606          98 :             new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
    5607          98 :         newAr->SetSelf(newAr);
    5608          98 :         return newAr;
    5609             :     }
    5610             : 
    5611         196 :     ~GDALExtractFieldMDArray() override
    5612          98 :     {
    5613          98 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
    5614         196 :     }
    5615             : 
    5616          51 :     bool IsWritable() const override
    5617             :     {
    5618          51 :         return m_poParent->IsWritable();
    5619             :     }
    5620             : 
    5621         309 :     const std::string &GetFilename() const override
    5622             :     {
    5623         309 :         return m_poParent->GetFilename();
    5624             :     }
    5625             : 
    5626             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5627         418 :     GetDimensions() const override
    5628             :     {
    5629         418 :         return m_poParent->GetDimensions();
    5630             :     }
    5631             : 
    5632         344 :     const GDALExtendedDataType &GetDataType() const override
    5633             :     {
    5634         344 :         return m_dt;
    5635             :     }
    5636             : 
    5637           2 :     const std::string &GetUnit() const override
    5638             :     {
    5639           2 :         return m_poParent->GetUnit();
    5640             :     }
    5641             : 
    5642           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5643             :     {
    5644           2 :         return m_poParent->GetSpatialRef();
    5645             :     }
    5646             : 
    5647          62 :     const void *GetRawNoDataValue() const override
    5648             :     {
    5649          62 :         const void *parentNoData = m_poParent->GetRawNoDataValue();
    5650          62 :         if (parentNoData == nullptr)
    5651           2 :             return nullptr;
    5652             : 
    5653          60 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
    5654          60 :         memset(&m_pabyNoData[0], 0, m_dt.GetSize());
    5655             : 
    5656         120 :         std::vector<std::unique_ptr<GDALEDTComponent>> comps;
    5657         120 :         comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
    5658         120 :             new GDALEDTComponent(m_srcCompName, 0, m_dt)));
    5659          60 :         auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
    5660         180 :                                                 std::move(comps)));
    5661             : 
    5662          60 :         GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
    5663          60 :                                         &m_pabyNoData[0], tmpDT);
    5664             : 
    5665          60 :         return &m_pabyNoData[0];
    5666             :     }
    5667             : 
    5668           2 :     double GetOffset(bool *pbHasOffset,
    5669             :                      GDALDataType *peStorageType) const override
    5670             :     {
    5671           2 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5672             :     }
    5673             : 
    5674           2 :     double GetScale(bool *pbHasScale,
    5675             :                     GDALDataType *peStorageType) const override
    5676             :     {
    5677           2 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5678             :     }
    5679             : 
    5680          52 :     std::vector<GUInt64> GetBlockSize() const override
    5681             :     {
    5682          52 :         return m_poParent->GetBlockSize();
    5683             :     }
    5684             : };
    5685             : 
    5686             : /************************************************************************/
    5687             : /*                             IRead()                                  */
    5688             : /************************************************************************/
    5689             : 
    5690         105 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
    5691             :                                     const size_t *count,
    5692             :                                     const GInt64 *arrayStep,
    5693             :                                     const GPtrDiff_t *bufferStride,
    5694             :                                     const GDALExtendedDataType &bufferDataType,
    5695             :                                     void *pDstBuffer) const
    5696             : {
    5697         210 :     std::vector<std::unique_ptr<GDALEDTComponent>> comps;
    5698         210 :     comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
    5699         210 :         new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
    5700             :     auto tmpDT(GDALExtendedDataType::Create(
    5701         210 :         std::string(), bufferDataType.GetSize(), std::move(comps)));
    5702             : 
    5703         105 :     return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
    5704         210 :                             tmpDT, pDstBuffer);
    5705             : }
    5706             : 
    5707             : /************************************************************************/
    5708             : /*                      CreateFieldNameExtractArray()                   */
    5709             : /************************************************************************/
    5710             : 
    5711             : static std::shared_ptr<GDALMDArray>
    5712          99 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
    5713             :                             const std::string &fieldName)
    5714             : {
    5715          99 :     CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
    5716          99 :     const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
    5717         235 :     for (const auto &comp : self->GetDataType().GetComponents())
    5718             :     {
    5719         234 :         if (comp->GetName() == fieldName)
    5720             :         {
    5721          98 :             srcComp = &comp;
    5722          98 :             break;
    5723             :         }
    5724             :     }
    5725          99 :     if (srcComp == nullptr)
    5726             :     {
    5727           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
    5728             :                  fieldName.c_str());
    5729           1 :         return nullptr;
    5730             :     }
    5731          98 :     return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
    5732             : }
    5733             : 
    5734             : /************************************************************************/
    5735             : /*                             GetView()                                */
    5736             : /************************************************************************/
    5737             : 
    5738             : // clang-format off
    5739             : /** Return a view of the array using slicing or field access.
    5740             :  *
    5741             :  * The slice expression uses the same syntax as NumPy basic slicing and
    5742             :  * indexing. See
    5743             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
    5744             :  * Or it can use field access by name. See
    5745             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
    5746             :  *
    5747             :  * Multiple [] bracket elements can be concatenated, with a slice expression
    5748             :  * or field name inside each.
    5749             :  *
    5750             :  * For basic slicing and indexing, inside each [] bracket element, a list of
    5751             :  * indexes that apply to successive source dimensions, can be specified, using
    5752             :  * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
    5753             :  * or newaxis, using a comma separator.
    5754             :  *
    5755             :  * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
    5756             :  * <ul>
    5757             :  * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
    5758             :  *     at index 1 in the first dimension, and index 2 in the second dimension
    5759             :  *     from the source array. That is 5</li>
    5760             :  * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
    5761             :  * implemented internally doing this intermediate slicing approach.</li>
    5762             :  * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
    5763             :  * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
    5764             :  *     first dimension. That is [4,5,6,7].</li>
    5765             :  * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
    5766             :  *     second dimension. That is [2,6].</li>
    5767             :  * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
    5768             :  * the second dimension. That is [[2],[6]].</li>
    5769             :  * <li>GetView("[::,2]"): Same as
    5770             :  * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
    5771             :  * ellipsis only expands to one dimension here.</li>
    5772             :  * <li>GetView("[:,::2]"):
    5773             :  * returns a 2-dimensional array, with even-indexed elements of the second
    5774             :  * dimension. That is [[0,2],[4,6]].</li>
    5775             :  * <li>GetView("[:,1::2]"): returns a
    5776             :  * 2-dimensional array, with odd-indexed elements of the second dimension. That
    5777             :  * is [[1,3],[5,7]].</li>
    5778             :  * <li>GetView("[:,1:3:]"): returns a 2-dimensional
    5779             :  * array, with elements of the second dimension with index in the range [1,3[.
    5780             :  * That is [[1,2],[5,6]].</li>
    5781             :  * <li>GetView("[::-1,:]"): returns a 2-dimensional
    5782             :  * array, with the values in first dimension reversed. That is
    5783             :  * [[4,5,6,7],[0,1,2,3]].</li>
    5784             :  * <li>GetView("[newaxis,...]"): returns a
    5785             :  * 3-dimensional array, with an additional dimension of size 1 put at the
    5786             :  * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
    5787             :  * </ul>
    5788             :  *
    5789             :  * One difference with NumPy behavior is that ranges that would result in
    5790             :  * zero elements are not allowed (dimensions of size 0 not being allowed in the
    5791             :  * GDAL multidimensional model).
    5792             :  *
    5793             :  * For field access, the syntax to use is ["field_name"] or ['field_name'].
    5794             :  * Multiple field specification is not supported currently.
    5795             :  *
    5796             :  * Both type of access can be combined, e.g. GetView("[1]['field_name']")
    5797             :  *
    5798             :  * \note When using the GDAL Python bindings, natural Python syntax can be
    5799             :  * used. That is ar[0,::,1]["foo"] will be internally translated to
    5800             :  * ar.GetView("[0,::,1]['foo']")
    5801             :  * \note When using the C++ API and integer indexing only, you may use the
    5802             :  * at(idx0, idx1, ...) method.
    5803             :  *
    5804             :  * The returned array holds a reference to the original one, and thus is
    5805             :  * a view of it (not a copy). If the content of the original array changes,
    5806             :  * the content of the view array too. When using basic slicing and indexing,
    5807             :  * the view can be written if the underlying array is writable.
    5808             :  *
    5809             :  * This is the same as the C function GDALMDArrayGetView()
    5810             :  *
    5811             :  * @param viewExpr Expression expressing basic slicing and indexing, or field
    5812             :  * access.
    5813             :  * @return a new array, that holds a reference to the original one, and thus is
    5814             :  * a view of it (not a copy), or nullptr in case of error.
    5815             :  */
    5816             : // clang-format on
    5817             : 
    5818             : std::shared_ptr<GDALMDArray>
    5819         659 : GDALMDArray::GetView(const std::string &viewExpr) const
    5820             : {
    5821        1318 :     std::vector<ViewSpec> viewSpecs;
    5822        1318 :     return GetView(viewExpr, true, viewSpecs);
    5823             : }
    5824             : 
    5825             : //! @cond Doxygen_Suppress
    5826             : std::shared_ptr<GDALMDArray>
    5827         731 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
    5828             :                      std::vector<ViewSpec> &viewSpecs) const
    5829             : {
    5830        1462 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    5831         731 :     if (!self)
    5832             :     {
    5833           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    5834             :                  "Driver implementation issue: m_pSelf not set !");
    5835           1 :         return nullptr;
    5836             :     }
    5837         730 :     std::string curExpr(viewExpr);
    5838             :     while (true)
    5839             :     {
    5840         733 :         if (curExpr.empty() || curExpr[0] != '[')
    5841             :         {
    5842           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    5843             :                      "Slice string should start with ['");
    5844         730 :             return nullptr;
    5845             :         }
    5846             : 
    5847         731 :         std::string fieldName;
    5848             :         size_t endExpr;
    5849         731 :         if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
    5850             :         {
    5851         103 :             if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
    5852             :             {
    5853           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5854             :                          "Field access not allowed on non-compound data type");
    5855           2 :                 return nullptr;
    5856             :             }
    5857         101 :             size_t idx = 2;
    5858         985 :             for (; idx < curExpr.size(); idx++)
    5859             :             {
    5860         984 :                 const char ch = curExpr[idx];
    5861         984 :                 if (ch == curExpr[1])
    5862         100 :                     break;
    5863         884 :                 if (ch == '\\' && idx + 1 < curExpr.size())
    5864             :                 {
    5865           1 :                     fieldName += curExpr[idx + 1];
    5866           1 :                     idx++;
    5867             :                 }
    5868             :                 else
    5869             :                 {
    5870         883 :                     fieldName += ch;
    5871             :                 }
    5872             :             }
    5873         101 :             if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
    5874             :             {
    5875           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5876             :                          "Invalid field access specification");
    5877           2 :                 return nullptr;
    5878             :             }
    5879          99 :             endExpr = idx + 1;
    5880             :         }
    5881             :         else
    5882             :         {
    5883         628 :             endExpr = curExpr.find(']');
    5884             :         }
    5885         727 :         if (endExpr == std::string::npos)
    5886             :         {
    5887           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
    5888           1 :             return nullptr;
    5889             :         }
    5890         726 :         if (endExpr == 1)
    5891             :         {
    5892           1 :             CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
    5893           1 :             return nullptr;
    5894             :         }
    5895         725 :         std::string activeSlice(curExpr.substr(1, endExpr - 1));
    5896             : 
    5897         725 :         if (!fieldName.empty())
    5898             :         {
    5899         198 :             ViewSpec viewSpec;
    5900          99 :             viewSpec.m_osFieldName = fieldName;
    5901          99 :             viewSpecs.emplace_back(std::move(viewSpec));
    5902             :         }
    5903             : 
    5904         725 :         auto newArray = !fieldName.empty()
    5905             :                             ? CreateFieldNameExtractArray(self, fieldName)
    5906             :                             : CreateSlicedArray(self, viewExpr, activeSlice,
    5907         725 :                                                 bRenameDimensions, viewSpecs);
    5908             : 
    5909         725 :         if (endExpr == curExpr.size() - 1)
    5910             :         {
    5911         722 :             return newArray;
    5912             :         }
    5913           3 :         self = std::move(newArray);
    5914           3 :         curExpr = curExpr.substr(endExpr + 1);
    5915           3 :     }
    5916             : }
    5917             : 
    5918             : //! @endcond
    5919             : 
    5920             : std::shared_ptr<GDALMDArray>
    5921          19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
    5922             : {
    5923          19 :     std::string osExpr("[");
    5924          19 :     bool bFirst = true;
    5925          45 :     for (const auto &idx : indices)
    5926             :     {
    5927          26 :         if (!bFirst)
    5928           7 :             osExpr += ',';
    5929          26 :         bFirst = false;
    5930          26 :         osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
    5931             :     }
    5932          57 :     return GetView(osExpr + ']');
    5933             : }
    5934             : 
    5935             : /************************************************************************/
    5936             : /*                            operator[]                                */
    5937             : /************************************************************************/
    5938             : 
    5939             : /** Return a view of the array using field access
    5940             :  *
    5941             :  * Equivalent of GetView("['fieldName']")
    5942             :  *
    5943             :  * \note When operating on a shared_ptr, use (*array)["fieldName"] syntax.
    5944             :  */
    5945             : std::shared_ptr<GDALMDArray>
    5946           2 : GDALMDArray::operator[](const std::string &fieldName) const
    5947             : {
    5948           2 :     return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
    5949           4 :                                             .replaceAll('\\', "\\\\")
    5950           4 :                                             .replaceAll('\'', "\\\'")
    5951           6 :                                             .c_str()));
    5952             : }
    5953             : 
    5954             : /************************************************************************/
    5955             : /*                      GDALMDArrayTransposed                           */
    5956             : /************************************************************************/
    5957             : 
    5958             : class GDALMDArrayTransposed final : public GDALPamMDArray
    5959             : {
    5960             :   private:
    5961             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5962             :     std::vector<int> m_anMapNewAxisToOldAxis{};
    5963             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    5964             : 
    5965             :     mutable std::vector<GUInt64> m_parentStart;
    5966             :     mutable std::vector<size_t> m_parentCount;
    5967             :     mutable std::vector<GInt64> m_parentStep;
    5968             :     mutable std::vector<GPtrDiff_t> m_parentStride;
    5969             : 
    5970             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
    5971             :                              const GInt64 *arrayStep,
    5972             :                              const GPtrDiff_t *bufferStride) const;
    5973             : 
    5974             :     static std::string
    5975          84 :     MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
    5976             :     {
    5977          84 :         std::string ret;
    5978          84 :         ret += '[';
    5979         312 :         for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
    5980             :         {
    5981         228 :             if (i > 0)
    5982         144 :                 ret += ',';
    5983         228 :             ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
    5984             :         }
    5985          84 :         ret += ']';
    5986          84 :         return ret;
    5987             :     }
    5988             : 
    5989             :   protected:
    5990          42 :     GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
    5991             :                           const std::vector<int> &anMapNewAxisToOldAxis,
    5992             :                           std::vector<std::shared_ptr<GDALDimension>> &&dims)
    5993          84 :         : GDALAbstractMDArray(std::string(),
    5994          84 :                               "Transposed view of " + poParent->GetFullName() +
    5995          84 :                                   " along " +
    5996          42 :                                   MappingToStr(anMapNewAxisToOldAxis)),
    5997          84 :           GDALPamMDArray(std::string(),
    5998          84 :                          "Transposed view of " + poParent->GetFullName() +
    5999         168 :                              " along " + MappingToStr(anMapNewAxisToOldAxis),
    6000          84 :                          GDALPamMultiDim::GetPAM(poParent),
    6001             :                          poParent->GetContext()),
    6002          42 :           m_poParent(std::move(poParent)),
    6003             :           m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
    6004          42 :           m_dims(std::move(dims)),
    6005          42 :           m_parentStart(m_poParent->GetDimensionCount()),
    6006          42 :           m_parentCount(m_poParent->GetDimensionCount()),
    6007          42 :           m_parentStep(m_poParent->GetDimensionCount()),
    6008         336 :           m_parentStride(m_poParent->GetDimensionCount())
    6009             :     {
    6010          42 :     }
    6011             : 
    6012             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6013             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    6014             :                const GDALExtendedDataType &bufferDataType,
    6015             :                void *pDstBuffer) const override;
    6016             : 
    6017             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    6018             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    6019             :                 const GDALExtendedDataType &bufferDataType,
    6020             :                 const void *pSrcBuffer) override;
    6021             : 
    6022             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6023             :                      CSLConstList papszOptions) const override;
    6024             : 
    6025             :   public:
    6026             :     static std::shared_ptr<GDALMDArrayTransposed>
    6027          42 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    6028             :            const std::vector<int> &anMapNewAxisToOldAxis)
    6029             :     {
    6030          42 :         const auto &parentDims(poParent->GetDimensions());
    6031          84 :         std::vector<std::shared_ptr<GDALDimension>> dims;
    6032         156 :         for (const auto iOldAxis : anMapNewAxisToOldAxis)
    6033             :         {
    6034         114 :             if (iOldAxis < 0)
    6035             :             {
    6036           1 :                 dims.push_back(std::make_shared<GDALDimension>(
    6037           2 :                     std::string(), "newaxis", std::string(), std::string(), 1));
    6038             :             }
    6039             :             else
    6040             :             {
    6041         113 :                 dims.emplace_back(parentDims[iOldAxis]);
    6042             :             }
    6043             :         }
    6044             : 
    6045             :         auto newAr(
    6046             :             std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
    6047          42 :                 poParent, anMapNewAxisToOldAxis, std::move(dims))));
    6048          42 :         newAr->SetSelf(newAr);
    6049          84 :         return newAr;
    6050             :     }
    6051             : 
    6052           1 :     bool IsWritable() const override
    6053             :     {
    6054           1 :         return m_poParent->IsWritable();
    6055             :     }
    6056             : 
    6057          84 :     const std::string &GetFilename() const override
    6058             :     {
    6059          84 :         return m_poParent->GetFilename();
    6060             :     }
    6061             : 
    6062             :     const std::vector<std::shared_ptr<GDALDimension>> &
    6063         358 :     GetDimensions() const override
    6064             :     {
    6065         358 :         return m_dims;
    6066             :     }
    6067             : 
    6068         141 :     const GDALExtendedDataType &GetDataType() const override
    6069             :     {
    6070         141 :         return m_poParent->GetDataType();
    6071             :     }
    6072             : 
    6073           4 :     const std::string &GetUnit() const override
    6074             :     {
    6075           4 :         return m_poParent->GetUnit();
    6076             :     }
    6077             : 
    6078           5 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    6079             :     {
    6080          10 :         auto poSrcSRS = m_poParent->GetSpatialRef();
    6081           5 :         if (!poSrcSRS)
    6082           2 :             return nullptr;
    6083           6 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
    6084           6 :         std::vector<int> dstMapping;
    6085           9 :         for (int srcAxis : srcMapping)
    6086             :         {
    6087           6 :             bool bFound = false;
    6088          14 :             for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
    6089             :             {
    6090          14 :                 if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
    6091             :                 {
    6092           6 :                     dstMapping.push_back(static_cast<int>(i) + 1);
    6093           6 :                     bFound = true;
    6094           6 :                     break;
    6095             :                 }
    6096             :             }
    6097           6 :             if (!bFound)
    6098             :             {
    6099           0 :                 dstMapping.push_back(0);
    6100             :             }
    6101             :         }
    6102           6 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
    6103           3 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
    6104           3 :         return poClone;
    6105             :     }
    6106             : 
    6107           4 :     const void *GetRawNoDataValue() const override
    6108             :     {
    6109           4 :         return m_poParent->GetRawNoDataValue();
    6110             :     }
    6111             : 
    6112             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
    6113             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
    6114             : 
    6115           4 :     double GetOffset(bool *pbHasOffset,
    6116             :                      GDALDataType *peStorageType) const override
    6117             :     {
    6118           4 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    6119             :     }
    6120             : 
    6121           4 :     double GetScale(bool *pbHasScale,
    6122             :                     GDALDataType *peStorageType) const override
    6123             :     {
    6124           4 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    6125             :     }
    6126             : 
    6127             :     // bool SetOffset(double dfOffset) override { return
    6128             :     // m_poParent->SetOffset(dfOffset); }
    6129             : 
    6130             :     // bool SetScale(double dfScale) override { return
    6131             :     // m_poParent->SetScale(dfScale); }
    6132             : 
    6133           3 :     std::vector<GUInt64> GetBlockSize() const override
    6134             :     {
    6135           3 :         std::vector<GUInt64> ret(GetDimensionCount());
    6136           6 :         const auto parentBlockSize(m_poParent->GetBlockSize());
    6137          11 :         for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
    6138             :         {
    6139           8 :             const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
    6140           8 :             if (iOldAxis >= 0)
    6141             :             {
    6142           7 :                 ret[i] = parentBlockSize[iOldAxis];
    6143             :             }
    6144             :         }
    6145           6 :         return ret;
    6146             :     }
    6147             : 
    6148             :     std::shared_ptr<GDALAttribute>
    6149           1 :     GetAttribute(const std::string &osName) const override
    6150             :     {
    6151           1 :         return m_poParent->GetAttribute(osName);
    6152             :     }
    6153             : 
    6154             :     std::vector<std::shared_ptr<GDALAttribute>>
    6155           6 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    6156             :     {
    6157           6 :         return m_poParent->GetAttributes(papszOptions);
    6158             :     }
    6159             : };
    6160             : 
    6161             : /************************************************************************/
    6162             : /*                         PrepareParentArrays()                        */
    6163             : /************************************************************************/
    6164             : 
    6165          47 : void GDALMDArrayTransposed::PrepareParentArrays(
    6166             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    6167             :     const GPtrDiff_t *bufferStride) const
    6168             : {
    6169         176 :     for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
    6170             :     {
    6171         129 :         const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
    6172         129 :         if (iOldAxis >= 0)
    6173             :         {
    6174         128 :             m_parentStart[iOldAxis] = arrayStartIdx[i];
    6175         128 :             m_parentCount[iOldAxis] = count[i];
    6176         128 :             if (arrayStep)  // only null when called from IAdviseRead()
    6177             :             {
    6178         126 :                 m_parentStep[iOldAxis] = arrayStep[i];
    6179             :             }
    6180         128 :             if (bufferStride)  // only null when called from IAdviseRead()
    6181             :             {
    6182         126 :                 m_parentStride[iOldAxis] = bufferStride[i];
    6183             :             }
    6184             :         }
    6185             :     }
    6186          47 : }
    6187             : 
    6188             : /************************************************************************/
    6189             : /*                             IRead()                                  */
    6190             : /************************************************************************/
    6191             : 
    6192          44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
    6193             :                                   const size_t *count, const GInt64 *arrayStep,
    6194             :                                   const GPtrDiff_t *bufferStride,
    6195             :                                   const GDALExtendedDataType &bufferDataType,
    6196             :                                   void *pDstBuffer) const
    6197             : {
    6198          44 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    6199          88 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
    6200          44 :                             m_parentStep.data(), m_parentStride.data(),
    6201          44 :                             bufferDataType, pDstBuffer);
    6202             : }
    6203             : 
    6204             : /************************************************************************/
    6205             : /*                            IWrite()                                  */
    6206             : /************************************************************************/
    6207             : 
    6208           2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
    6209             :                                    const size_t *count, const GInt64 *arrayStep,
    6210             :                                    const GPtrDiff_t *bufferStride,
    6211             :                                    const GDALExtendedDataType &bufferDataType,
    6212             :                                    const void *pSrcBuffer)
    6213             : {
    6214           2 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    6215           4 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
    6216           2 :                              m_parentStep.data(), m_parentStride.data(),
    6217           2 :                              bufferDataType, pSrcBuffer);
    6218             : }
    6219             : 
    6220             : /************************************************************************/
    6221             : /*                             IAdviseRead()                            */
    6222             : /************************************************************************/
    6223             : 
    6224           1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
    6225             :                                         const size_t *count,
    6226             :                                         CSLConstList papszOptions) const
    6227             : {
    6228           1 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
    6229           1 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
    6230           1 :                                   papszOptions);
    6231             : }
    6232             : 
    6233             : /************************************************************************/
    6234             : /*                           Transpose()                                */
    6235             : /************************************************************************/
    6236             : 
    6237             : /** Return a view of the array whose axis have been reordered.
    6238             :  *
    6239             :  * The anMapNewAxisToOldAxis parameter should contain all the values between 0
    6240             :  * and GetDimensionCount() - 1, and each only once.
    6241             :  * -1 can be used as a special index value to ask for the insertion of a new
    6242             :  * axis of size 1.
    6243             :  * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
    6244             :  * index of one of its dimension, it corresponds to the axis of index
    6245             :  * anMapNewAxisToOldAxis[i] from the current array.
    6246             :  *
    6247             :  * This is similar to the numpy.transpose() method
    6248             :  *
    6249             :  * The returned array holds a reference to the original one, and thus is
    6250             :  * a view of it (not a copy). If the content of the original array changes,
    6251             :  * the content of the view array too. The view can be written if the underlying
    6252             :  * array is writable.
    6253             :  *
    6254             :  * Note that I/O performance in such a transposed view might be poor.
    6255             :  *
    6256             :  * This is the same as the C function GDALMDArrayTranspose().
    6257             :  *
    6258             :  * @return a new array, that holds a reference to the original one, and thus is
    6259             :  * a view of it (not a copy), or nullptr in case of error.
    6260             :  */
    6261             : std::shared_ptr<GDALMDArray>
    6262          50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
    6263             : {
    6264         100 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    6265          50 :     if (!self)
    6266             :     {
    6267           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6268             :                  "Driver implementation issue: m_pSelf not set !");
    6269           0 :         return nullptr;
    6270             :     }
    6271          50 :     const int nDims = static_cast<int>(GetDimensionCount());
    6272         100 :     std::vector<bool> alreadyUsedOldAxis(nDims, false);
    6273          50 :     int nCountOldAxis = 0;
    6274         179 :     for (const auto iOldAxis : anMapNewAxisToOldAxis)
    6275             :     {
    6276         133 :         if (iOldAxis < -1 || iOldAxis >= nDims)
    6277             :         {
    6278           3 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
    6279           4 :             return nullptr;
    6280             :         }
    6281         130 :         if (iOldAxis >= 0)
    6282             :         {
    6283         128 :             if (alreadyUsedOldAxis[iOldAxis])
    6284             :             {
    6285           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
    6286             :                          iOldAxis);
    6287           1 :                 return nullptr;
    6288             :             }
    6289         127 :             alreadyUsedOldAxis[iOldAxis] = true;
    6290         127 :             nCountOldAxis++;
    6291             :         }
    6292             :     }
    6293          46 :     if (nCountOldAxis != nDims)
    6294             :     {
    6295           4 :         CPLError(CE_Failure, CPLE_AppDefined,
    6296             :                  "One or several original axis missing");
    6297           4 :         return nullptr;
    6298             :     }
    6299          42 :     return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
    6300             : }
    6301             : 
    6302             : /************************************************************************/
    6303             : /*                             IRead()                                  */
    6304             : /************************************************************************/
    6305             : 
    6306          16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
    6307             :                                 const size_t *count, const GInt64 *arrayStep,
    6308             :                                 const GPtrDiff_t *bufferStride,
    6309             :                                 const GDALExtendedDataType &bufferDataType,
    6310             :                                 void *pDstBuffer) const
    6311             : {
    6312          16 :     const double dfScale = m_dfScale;
    6313          16 :     const double dfOffset = m_dfOffset;
    6314          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
    6315             :     const auto dtDouble =
    6316          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
    6317          16 :     const size_t nDTSize = dtDouble.GetSize();
    6318          16 :     const bool bTempBufferNeeded = (dtDouble != bufferDataType);
    6319             : 
    6320          16 :     double adfSrcNoData[2] = {0, 0};
    6321          16 :     if (m_bHasNoData)
    6322             :     {
    6323           9 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
    6324           9 :                                         m_poParent->GetDataType(),
    6325             :                                         &adfSrcNoData[0], dtDouble);
    6326             :     }
    6327             : 
    6328          16 :     const auto nDims = GetDimensions().size();
    6329          16 :     if (nDims == 0)
    6330             :     {
    6331             :         double adfVal[2];
    6332           9 :         if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
    6333             :                               dtDouble, &adfVal[0]))
    6334             :         {
    6335           0 :             return false;
    6336             :         }
    6337           9 :         if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
    6338             :         {
    6339           6 :             adfVal[0] = adfVal[0] * dfScale + dfOffset;
    6340           6 :             if (bDTIsComplex)
    6341             :             {
    6342           2 :                 adfVal[1] = adfVal[1] * dfScale + dfOffset;
    6343             :             }
    6344           6 :             GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
    6345             :                                             bufferDataType);
    6346             :         }
    6347             :         else
    6348             :         {
    6349           3 :             GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
    6350             :                                             pDstBuffer, bufferDataType);
    6351             :         }
    6352           9 :         return true;
    6353             :     }
    6354             : 
    6355          14 :     std::vector<GPtrDiff_t> actualBufferStrideVector;
    6356           7 :     const GPtrDiff_t *actualBufferStridePtr = bufferStride;
    6357           7 :     void *pTempBuffer = pDstBuffer;
    6358           7 :     if (bTempBufferNeeded)
    6359             :     {
    6360           2 :         size_t nElts = 1;
    6361           2 :         actualBufferStrideVector.resize(nDims);
    6362           7 :         for (size_t i = 0; i < nDims; i++)
    6363           5 :             nElts *= count[i];
    6364           2 :         actualBufferStrideVector.back() = 1;
    6365           5 :         for (size_t i = nDims - 1; i > 0;)
    6366             :         {
    6367           3 :             --i;
    6368           3 :             actualBufferStrideVector[i] =
    6369           3 :                 actualBufferStrideVector[i + 1] * count[i + 1];
    6370             :         }
    6371           2 :         actualBufferStridePtr = actualBufferStrideVector.data();
    6372           2 :         pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
    6373           2 :         if (!pTempBuffer)
    6374           0 :             return false;
    6375             :     }
    6376           7 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
    6377             :                           actualBufferStridePtr, dtDouble, pTempBuffer))
    6378             :     {
    6379           0 :         if (bTempBufferNeeded)
    6380           0 :             VSIFree(pTempBuffer);
    6381           0 :         return false;
    6382             :     }
    6383             : 
    6384             :     struct Stack
    6385             :     {
    6386             :         size_t nIters = 0;
    6387             :         double *src_ptr = nullptr;
    6388             :         GByte *dst_ptr = nullptr;
    6389             :         GPtrDiff_t src_inc_offset = 0;
    6390             :         GPtrDiff_t dst_inc_offset = 0;
    6391             :     };
    6392             : 
    6393           7 :     std::vector<Stack> stack(nDims);
    6394           7 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    6395          23 :     for (size_t i = 0; i < nDims; i++)
    6396             :     {
    6397          32 :         stack[i].src_inc_offset =
    6398          16 :             actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
    6399          16 :         stack[i].dst_inc_offset =
    6400          16 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6401             :     }
    6402           7 :     stack[0].src_ptr = static_cast<double *>(pTempBuffer);
    6403           7 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    6404             : 
    6405           7 :     size_t dimIdx = 0;
    6406           7 :     const size_t nDimsMinus1 = nDims - 1;
    6407             :     GByte abyDstNoData[16];
    6408           7 :     CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
    6409           7 :     GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
    6410             :                                     bufferDataType);
    6411             : 
    6412          37 : lbl_next_depth:
    6413          37 :     if (dimIdx == nDimsMinus1)
    6414             :     {
    6415          25 :         auto nIters = count[dimIdx];
    6416          25 :         double *padfVal = stack[dimIdx].src_ptr;
    6417          25 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
    6418             :         while (true)
    6419             :         {
    6420          92 :             if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
    6421             :             {
    6422          88 :                 padfVal[0] = padfVal[0] * dfScale + dfOffset;
    6423          88 :                 if (bDTIsComplex)
    6424             :                 {
    6425           1 :                     padfVal[1] = padfVal[1] * dfScale + dfOffset;
    6426             :                 }
    6427          88 :                 if (bTempBufferNeeded)
    6428             :                 {
    6429          29 :                     GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
    6430             :                                                     dst_ptr, bufferDataType);
    6431             :                 }
    6432             :             }
    6433             :             else
    6434             :             {
    6435           4 :                 memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
    6436             :             }
    6437             : 
    6438          92 :             if ((--nIters) == 0)
    6439          25 :                 break;
    6440          67 :             padfVal += stack[dimIdx].src_inc_offset;
    6441          67 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    6442             :         }
    6443             :     }
    6444             :     else
    6445             :     {
    6446          12 :         stack[dimIdx].nIters = count[dimIdx];
    6447             :         while (true)
    6448             :         {
    6449          30 :             dimIdx++;
    6450          30 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    6451          30 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6452          30 :             goto lbl_next_depth;
    6453          30 :         lbl_return_to_caller:
    6454          30 :             dimIdx--;
    6455          30 :             if ((--stack[dimIdx].nIters) == 0)
    6456          12 :                 break;
    6457          18 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    6458          18 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6459             :         }
    6460             :     }
    6461          37 :     if (dimIdx > 0)
    6462          30 :         goto lbl_return_to_caller;
    6463             : 
    6464           7 :     if (bTempBufferNeeded)
    6465           2 :         VSIFree(pTempBuffer);
    6466           7 :     return true;
    6467             : }
    6468             : 
    6469             : /************************************************************************/
    6470             : /*                             IWrite()                                 */
    6471             : /************************************************************************/
    6472             : 
    6473          16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
    6474             :                                  const size_t *count, const GInt64 *arrayStep,
    6475             :                                  const GPtrDiff_t *bufferStride,
    6476             :                                  const GDALExtendedDataType &bufferDataType,
    6477             :                                  const void *pSrcBuffer)
    6478             : {
    6479          16 :     const double dfScale = m_dfScale;
    6480          16 :     const double dfOffset = m_dfOffset;
    6481          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
    6482             :     const auto dtDouble =
    6483          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
    6484          16 :     const size_t nDTSize = dtDouble.GetSize();
    6485          16 :     const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
    6486             :     const bool bSelfAndParentHaveNoData =
    6487          16 :         m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
    6488          16 :     double dfNoData = 0;
    6489          16 :     if (m_bHasNoData)
    6490             :     {
    6491           7 :         GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
    6492             :                         &dfNoData, GDT_Float64, 0, 1);
    6493             :     }
    6494             : 
    6495          16 :     double adfSrcNoData[2] = {0, 0};
    6496          16 :     if (bSelfAndParentHaveNoData)
    6497             :     {
    6498           7 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
    6499           7 :                                         m_poParent->GetDataType(),
    6500             :                                         &adfSrcNoData[0], dtDouble);
    6501             :     }
    6502             : 
    6503          16 :     const auto nDims = GetDimensions().size();
    6504          16 :     if (nDims == 0)
    6505             :     {
    6506             :         double adfVal[2];
    6507          10 :         GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
    6508             :                                         dtDouble);
    6509          16 :         if (bSelfAndParentHaveNoData &&
    6510           6 :             (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
    6511             :         {
    6512           4 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
    6513           2 :                                      bufferStride, m_poParent->GetDataType(),
    6514           4 :                                      m_poParent->GetRawNoDataValue());
    6515             :         }
    6516             :         else
    6517             :         {
    6518           8 :             adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
    6519           8 :             if (bDTIsComplex)
    6520             :             {
    6521           2 :                 adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
    6522             :             }
    6523           8 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
    6524           8 :                                      bufferStride, dtDouble, &adfVal[0]);
    6525             :         }
    6526             :     }
    6527             : 
    6528          12 :     std::vector<GPtrDiff_t> tmpBufferStrideVector;
    6529           6 :     size_t nElts = 1;
    6530           6 :     tmpBufferStrideVector.resize(nDims);
    6531          20 :     for (size_t i = 0; i < nDims; i++)
    6532          14 :         nElts *= count[i];
    6533           6 :     tmpBufferStrideVector.back() = 1;
    6534          14 :     for (size_t i = nDims - 1; i > 0;)
    6535             :     {
    6536           8 :         --i;
    6537           8 :         tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
    6538             :     }
    6539           6 :     const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
    6540           6 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
    6541           6 :     if (!pTempBuffer)
    6542           0 :         return false;
    6543             : 
    6544             :     struct Stack
    6545             :     {
    6546             :         size_t nIters = 0;
    6547             :         double *dst_ptr = nullptr;
    6548             :         const GByte *src_ptr = nullptr;
    6549             :         GPtrDiff_t src_inc_offset = 0;
    6550             :         GPtrDiff_t dst_inc_offset = 0;
    6551             :     };
    6552             : 
    6553           6 :     std::vector<Stack> stack(nDims);
    6554           6 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    6555          20 :     for (size_t i = 0; i < nDims; i++)
    6556             :     {
    6557          28 :         stack[i].dst_inc_offset =
    6558          14 :             tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
    6559          14 :         stack[i].src_inc_offset =
    6560          14 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6561             :     }
    6562           6 :     stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
    6563           6 :     stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
    6564             : 
    6565           6 :     size_t dimIdx = 0;
    6566           6 :     const size_t nDimsMinus1 = nDims - 1;
    6567             : 
    6568          34 : lbl_next_depth:
    6569          34 :     if (dimIdx == nDimsMinus1)
    6570             :     {
    6571          23 :         auto nIters = count[dimIdx];
    6572          23 :         double *dst_ptr = stack[dimIdx].dst_ptr;
    6573          23 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
    6574             :         while (true)
    6575             :         {
    6576             :             double adfVal[2];
    6577             :             const double *padfSrcVal;
    6578          86 :             if (bIsBufferDataTypeNativeDataType)
    6579             :             {
    6580          50 :                 padfSrcVal = reinterpret_cast<const double *>(src_ptr);
    6581             :             }
    6582             :             else
    6583             :             {
    6584          36 :                 GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
    6585             :                                                 &adfVal[0], dtDouble);
    6586          36 :                 padfSrcVal = adfVal;
    6587             :             }
    6588             : 
    6589         148 :             if (bSelfAndParentHaveNoData &&
    6590          62 :                 (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
    6591             :             {
    6592           3 :                 dst_ptr[0] = adfSrcNoData[0];
    6593           3 :                 if (bDTIsComplex)
    6594             :                 {
    6595           1 :                     dst_ptr[1] = adfSrcNoData[1];
    6596             :                 }
    6597             :             }
    6598             :             else
    6599             :             {
    6600          83 :                 dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
    6601          83 :                 if (bDTIsComplex)
    6602             :                 {
    6603           1 :                     dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
    6604             :                 }
    6605             :             }
    6606             : 
    6607          86 :             if ((--nIters) == 0)
    6608          23 :                 break;
    6609          63 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    6610          63 :             src_ptr += stack[dimIdx].src_inc_offset;
    6611          63 :         }
    6612             :     }
    6613             :     else
    6614             :     {
    6615          11 :         stack[dimIdx].nIters = count[dimIdx];
    6616             :         while (true)
    6617             :         {
    6618          28 :             dimIdx++;
    6619          28 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    6620          28 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6621          28 :             goto lbl_next_depth;
    6622          28 :         lbl_return_to_caller:
    6623          28 :             dimIdx--;
    6624          28 :             if ((--stack[dimIdx].nIters) == 0)
    6625          11 :                 break;
    6626          17 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    6627          17 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6628             :         }
    6629             :     }
    6630          34 :     if (dimIdx > 0)
    6631          28 :         goto lbl_return_to_caller;
    6632             : 
    6633             :     // If the parent array is not double/complex-double, then convert the
    6634             :     // values to it, before calling Write(), as some implementations can be
    6635             :     // very slow when doing the type conversion.
    6636           6 :     const auto &eParentDT = m_poParent->GetDataType();
    6637           6 :     const size_t nParentDTSize = eParentDT.GetSize();
    6638           6 :     if (nParentDTSize <= nDTSize / 2)
    6639             :     {
    6640             :         // Copy in-place by making sure that source and target do not overlap
    6641           6 :         const auto eNumericDT = dtDouble.GetNumericDataType();
    6642           6 :         const auto eParentNumericDT = eParentDT.GetNumericDataType();
    6643             : 
    6644             :         // Copy first element
    6645             :         {
    6646           6 :             std::vector<GByte> abyTemp(nParentDTSize);
    6647           6 :             GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
    6648           6 :                             static_cast<int>(nDTSize), &abyTemp[0],
    6649             :                             eParentNumericDT, static_cast<int>(nParentDTSize),
    6650             :                             1);
    6651           6 :             memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
    6652             :         }
    6653             :         // Remaining elements
    6654          86 :         for (size_t i = 1; i < nElts; ++i)
    6655             :         {
    6656          80 :             GDALCopyWords64(
    6657          80 :                 static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
    6658          80 :                 static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
    6659             :                 eParentNumericDT, 0, 1);
    6660             :         }
    6661             :     }
    6662             : 
    6663             :     const bool ret =
    6664           6 :         m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
    6665             :                           eParentDT, pTempBuffer);
    6666             : 
    6667           6 :     VSIFree(pTempBuffer);
    6668           6 :     return ret;
    6669             : }
    6670             : 
    6671             : /************************************************************************/
    6672             : /*                           GetUnscaled()                              */
    6673             : /************************************************************************/
    6674             : 
    6675             : /** Return an array that is the unscaled version of the current one.
    6676             :  *
    6677             :  * That is each value of the unscaled array will be
    6678             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    6679             :  *
    6680             :  * Starting with GDAL 3.3, the Write() method is implemented and will convert
    6681             :  * from unscaled values to raw values.
    6682             :  *
    6683             :  * This is the same as the C function GDALMDArrayGetUnscaled().
    6684             :  *
    6685             :  * @param dfOverriddenScale Custom scale value instead of GetScale()
    6686             :  * @param dfOverriddenOffset Custom offset value instead of GetOffset()
    6687             :  * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
    6688             :  * @return a new array, that holds a reference to the original one, and thus is
    6689             :  * a view of it (not a copy), or nullptr in case of error.
    6690             :  */
    6691             : std::shared_ptr<GDALMDArray>
    6692          17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
    6693             :                          double dfOverriddenDstNodata) const
    6694             : {
    6695          34 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    6696          17 :     if (!self)
    6697             :     {
    6698           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6699             :                  "Driver implementation issue: m_pSelf not set !");
    6700           0 :         return nullptr;
    6701             :     }
    6702          17 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    6703             :     {
    6704           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6705             :                  "GetUnscaled() only supports numeric data type");
    6706           0 :         return nullptr;
    6707             :     }
    6708             :     const double dfScale =
    6709          17 :         std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
    6710             :     const double dfOffset =
    6711          17 :         std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
    6712          17 :     if (dfScale == 1.0 && dfOffset == 0.0)
    6713           4 :         return self;
    6714             : 
    6715          13 :     GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
    6716          13 :                            ? GDT_CFloat64
    6717          13 :                            : GDT_Float64;
    6718          13 :     if (dfOverriddenScale == -1 && dfOverriddenOffset == 0)
    6719             :     {
    6720           1 :         if (GetDataType().GetNumericDataType() == GDT_Float16)
    6721           0 :             eDT = GDT_Float16;
    6722           1 :         if (GetDataType().GetNumericDataType() == GDT_Float32)
    6723           1 :             eDT = GDT_Float32;
    6724             :     }
    6725             : 
    6726          26 :     return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
    6727          13 :                                        dfOverriddenDstNodata, eDT);
    6728             : }
    6729             : 
    6730             : /************************************************************************/
    6731             : /*                         GDALMDArrayMask                              */
    6732             : /************************************************************************/
    6733             : 
    6734             : class GDALMDArrayMask final : public GDALPamMDArray
    6735             : {
    6736             :   private:
    6737             :     std::shared_ptr<GDALMDArray> m_poParent{};
    6738             :     GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_UInt8)};
    6739             :     double m_dfMissingValue = 0.0;
    6740             :     bool m_bHasMissingValue = false;
    6741             :     double m_dfFillValue = 0.0;
    6742             :     bool m_bHasFillValue = false;
    6743             :     double m_dfValidMin = 0.0;
    6744             :     bool m_bHasValidMin = false;
    6745             :     double m_dfValidMax = 0.0;
    6746             :     bool m_bHasValidMax = false;
    6747             :     std::vector<uint32_t> m_anValidFlagMasks{};
    6748             :     std::vector<uint32_t> m_anValidFlagValues{};
    6749             : 
    6750             :     bool Init(CSLConstList papszOptions);
    6751             : 
    6752             :     template <typename Type>
    6753             :     void
    6754             :     ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
    6755             :                  const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    6756             :                  const void *pTempBuffer,
    6757             :                  const GDALExtendedDataType &oTmpBufferDT,
    6758             :                  const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
    6759             : 
    6760             :   protected:
    6761          48 :     explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
    6762          96 :         : GDALAbstractMDArray(std::string(),
    6763          96 :                               "Mask of " + poParent->GetFullName()),
    6764          96 :           GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
    6765          96 :                          GDALPamMultiDim::GetPAM(poParent),
    6766             :                          poParent->GetContext()),
    6767         240 :           m_poParent(std::move(poParent))
    6768             :     {
    6769          48 :     }
    6770             : 
    6771             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6772             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    6773             :                const GDALExtendedDataType &bufferDataType,
    6774             :                void *pDstBuffer) const override;
    6775             : 
    6776           0 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6777             :                      CSLConstList papszOptions) const override
    6778             :     {
    6779           0 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
    6780             :     }
    6781             : 
    6782             :   public:
    6783             :     static std::shared_ptr<GDALMDArrayMask>
    6784             :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    6785             :            CSLConstList papszOptions);
    6786             : 
    6787           1 :     bool IsWritable() const override
    6788             :     {
    6789           1 :         return false;
    6790             :     }
    6791             : 
    6792          54 :     const std::string &GetFilename() const override
    6793             :     {
    6794          54 :         return m_poParent->GetFilename();
    6795             :     }
    6796             : 
    6797             :     const std::vector<std::shared_ptr<GDALDimension>> &
    6798         382 :     GetDimensions() const override
    6799             :     {
    6800         382 :         return m_poParent->GetDimensions();
    6801             :     }
    6802             : 
    6803         138 :     const GDALExtendedDataType &GetDataType() const override
    6804             :     {
    6805         138 :         return m_dt;
    6806             :     }
    6807             : 
    6808           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    6809             :     {
    6810           1 :         return m_poParent->GetSpatialRef();
    6811             :     }
    6812             : 
    6813           2 :     std::vector<GUInt64> GetBlockSize() const override
    6814             :     {
    6815           2 :         return m_poParent->GetBlockSize();
    6816             :     }
    6817             : };
    6818             : 
    6819             : /************************************************************************/
    6820             : /*                    GDALMDArrayMask::Create()                         */
    6821             : /************************************************************************/
    6822             : 
    6823             : /* static */ std::shared_ptr<GDALMDArrayMask>
    6824          48 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
    6825             :                         CSLConstList papszOptions)
    6826             : {
    6827          96 :     auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
    6828          48 :     newAr->SetSelf(newAr);
    6829          48 :     if (!newAr->Init(papszOptions))
    6830           6 :         return nullptr;
    6831          42 :     return newAr;
    6832             : }
    6833             : 
    6834             : /************************************************************************/
    6835             : /*                    GDALMDArrayMask::Init()                           */
    6836             : /************************************************************************/
    6837             : 
    6838          48 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
    6839             : {
    6840             :     const auto GetSingleValNumericAttr =
    6841         192 :         [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
    6842             :     {
    6843         576 :         auto poAttr = m_poParent->GetAttribute(pszAttrName);
    6844         192 :         if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
    6845             :         {
    6846          22 :             const auto anDimSizes = poAttr->GetDimensionsSize();
    6847          21 :             if (anDimSizes.empty() ||
    6848          10 :                 (anDimSizes.size() == 1 && anDimSizes[0] == 1))
    6849             :             {
    6850          11 :                 bHasVal = true;
    6851          11 :                 dfVal = poAttr->ReadAsDouble();
    6852             :             }
    6853             :         }
    6854         192 :     };
    6855             : 
    6856          48 :     GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
    6857          48 :                             m_dfMissingValue);
    6858          48 :     GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
    6859          48 :     GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
    6860          48 :     GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
    6861             : 
    6862             :     {
    6863         144 :         auto poValidRange = m_poParent->GetAttribute("valid_range");
    6864          54 :         if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
    6865          60 :             poValidRange->GetDimensionsSize()[0] == 2 &&
    6866           6 :             poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
    6867             :         {
    6868           6 :             m_bHasValidMin = true;
    6869           6 :             m_bHasValidMax = true;
    6870           6 :             auto vals = poValidRange->ReadAsDoubleArray();
    6871           6 :             CPLAssert(vals.size() == 2);
    6872           6 :             m_dfValidMin = vals[0];
    6873           6 :             m_dfValidMax = vals[1];
    6874             :         }
    6875             :     }
    6876             : 
    6877             :     // Take into account
    6878             :     // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
    6879             :     // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
    6880             :     const char *pszUnmaskFlags =
    6881          48 :         CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
    6882          48 :     if (pszUnmaskFlags)
    6883             :     {
    6884             :         const auto IsScalarStringAttr =
    6885          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
    6886             :         {
    6887          26 :             return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
    6888          26 :                    (poAttr->GetDimensionsSize().empty() ||
    6889          13 :                     (poAttr->GetDimensionsSize().size() == 1 &&
    6890          26 :                      poAttr->GetDimensionsSize()[0] == 1));
    6891             :         };
    6892             : 
    6893          28 :         auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
    6894          14 :         if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
    6895             :         {
    6896           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6897             :                      "UNMASK_FLAGS option specified but array has no "
    6898             :                      "flag_meanings attribute");
    6899           1 :             return false;
    6900             :         }
    6901          13 :         const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
    6902          13 :         if (!pszFlagMeanings)
    6903             :         {
    6904           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6905             :                      "Cannot read flag_meanings attribute");
    6906           1 :             return false;
    6907             :         }
    6908             : 
    6909             :         const auto IsSingleDimNumericAttr =
    6910          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
    6911             :         {
    6912          26 :             return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
    6913          26 :                    poAttr->GetDimensionsSize().size() == 1;
    6914             :         };
    6915             : 
    6916          24 :         auto poFlagValues = m_poParent->GetAttribute("flag_values");
    6917             :         const bool bHasFlagValues =
    6918          12 :             poFlagValues && IsSingleDimNumericAttr(poFlagValues);
    6919             : 
    6920          24 :         auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
    6921             :         const bool bHasFlagMasks =
    6922          12 :             poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
    6923             : 
    6924          12 :         if (!bHasFlagValues && !bHasFlagMasks)
    6925             :         {
    6926           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6927             :                      "Cannot find flag_values and/or flag_masks attribute");
    6928           1 :             return false;
    6929             :         }
    6930             : 
    6931             :         const CPLStringList aosUnmaskFlags(
    6932          11 :             CSLTokenizeString2(pszUnmaskFlags, ",", 0));
    6933             :         const CPLStringList aosFlagMeanings(
    6934          11 :             CSLTokenizeString2(pszFlagMeanings, " ", 0));
    6935             : 
    6936          11 :         if (bHasFlagValues)
    6937             :         {
    6938           7 :             const auto eType = poFlagValues->GetDataType().GetNumericDataType();
    6939             :             // We could support Int64 or UInt64, but more work...
    6940           7 :             if (eType != GDT_UInt8 && eType != GDT_Int8 &&
    6941           7 :                 eType != GDT_UInt16 && eType != GDT_Int16 &&
    6942           0 :                 eType != GDT_UInt32 && eType != GDT_Int32)
    6943             :             {
    6944           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    6945             :                          "Unsupported data type for flag_values attribute: %s",
    6946             :                          GDALGetDataTypeName(eType));
    6947           0 :                 return false;
    6948             :             }
    6949             :         }
    6950             : 
    6951          11 :         if (bHasFlagMasks)
    6952             :         {
    6953           6 :             const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
    6954             :             // We could support Int64 or UInt64, but more work...
    6955           6 :             if (eType != GDT_UInt8 && eType != GDT_Int8 &&
    6956           6 :                 eType != GDT_UInt16 && eType != GDT_Int16 &&
    6957           0 :                 eType != GDT_UInt32 && eType != GDT_Int32)
    6958             :             {
    6959           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    6960             :                          "Unsupported data type for flag_masks attribute: %s",
    6961             :                          GDALGetDataTypeName(eType));
    6962           0 :                 return false;
    6963             :             }
    6964             :         }
    6965             : 
    6966             :         const std::vector<double> adfValues(
    6967             :             bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
    6968          11 :                            : std::vector<double>());
    6969             :         const std::vector<double> adfMasks(
    6970             :             bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
    6971          11 :                           : std::vector<double>());
    6972             : 
    6973          18 :         if (bHasFlagValues &&
    6974           7 :             adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
    6975             :         {
    6976           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6977             :                      "Number of values in flag_values attribute is different "
    6978             :                      "from the one in flag_meanings");
    6979           1 :             return false;
    6980             :         }
    6981             : 
    6982          16 :         if (bHasFlagMasks &&
    6983           6 :             adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
    6984             :         {
    6985           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6986             :                      "Number of values in flag_masks attribute is different "
    6987             :                      "from the one in flag_meanings");
    6988           1 :             return false;
    6989             :         }
    6990             : 
    6991          19 :         for (int i = 0; i < aosUnmaskFlags.size(); ++i)
    6992             :         {
    6993          11 :             const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
    6994          11 :             if (nIdxFlag < 0)
    6995             :             {
    6996           1 :                 CPLError(
    6997             :                     CE_Failure, CPLE_AppDefined,
    6998             :                     "Cannot fing flag %s in flag_meanings = '%s' attribute",
    6999             :                     aosUnmaskFlags[i], pszFlagMeanings);
    7000           1 :                 return false;
    7001             :             }
    7002             : 
    7003          10 :             if (bHasFlagValues && adfValues[nIdxFlag] < 0)
    7004             :             {
    7005           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    7006             :                          "Invalid value in flag_values[%d] = %f", nIdxFlag,
    7007           0 :                          adfValues[nIdxFlag]);
    7008           0 :                 return false;
    7009             :             }
    7010             : 
    7011          10 :             if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
    7012             :             {
    7013           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    7014             :                          "Invalid value in flag_masks[%d] = %f", nIdxFlag,
    7015           0 :                          adfMasks[nIdxFlag]);
    7016           0 :                 return false;
    7017             :             }
    7018             : 
    7019          10 :             if (bHasFlagValues)
    7020             :             {
    7021          12 :                 m_anValidFlagValues.push_back(
    7022           6 :                     static_cast<uint32_t>(adfValues[nIdxFlag]));
    7023             :             }
    7024             : 
    7025          10 :             if (bHasFlagMasks)
    7026             :             {
    7027          12 :                 m_anValidFlagMasks.push_back(
    7028           6 :                     static_cast<uint32_t>(adfMasks[nIdxFlag]));
    7029             :             }
    7030             :         }
    7031             :     }
    7032             : 
    7033          42 :     return true;
    7034             : }
    7035             : 
    7036             : /************************************************************************/
    7037             : /*                             IRead()                                  */
    7038             : /************************************************************************/
    7039             : 
    7040          51 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    7041             :                             const GInt64 *arrayStep,
    7042             :                             const GPtrDiff_t *bufferStride,
    7043             :                             const GDALExtendedDataType &bufferDataType,
    7044             :                             void *pDstBuffer) const
    7045             : {
    7046          51 :     if (bufferDataType.GetClass() != GEDTC_NUMERIC)
    7047             :     {
    7048           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    7049             :                  "%s: only reading to a numeric data type is supported",
    7050             :                  __func__);
    7051           0 :         return false;
    7052             :     }
    7053          51 :     size_t nElts = 1;
    7054          51 :     const size_t nDims = GetDimensionCount();
    7055         102 :     std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
    7056         139 :     for (size_t i = 0; i < nDims; i++)
    7057          88 :         nElts *= count[i];
    7058          51 :     if (nDims > 0)
    7059             :     {
    7060          46 :         tmpBufferStrideVector.back() = 1;
    7061          88 :         for (size_t i = nDims - 1; i > 0;)
    7062             :         {
    7063          42 :             --i;
    7064          42 :             tmpBufferStrideVector[i] =
    7065          42 :                 tmpBufferStrideVector[i + 1] * count[i + 1];
    7066             :         }
    7067             :     }
    7068             : 
    7069             :     /* Optimized case: if we are an integer data type and that there is no */
    7070             :     /* attribute that can be used to set mask = 0, then fill the mask buffer */
    7071             :     /* directly */
    7072          49 :     if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
    7073          74 :         !m_bHasValidMax && m_anValidFlagValues.empty() &&
    7074          34 :         m_anValidFlagMasks.empty() &&
    7075         111 :         m_poParent->GetRawNoDataValue() == nullptr &&
    7076          11 :         GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
    7077             :     {
    7078           7 :         const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
    7079           7 :         if (bBufferDataTypeIsByte)  // Byte case
    7080             :         {
    7081           4 :             bool bContiguous = true;
    7082          10 :             for (size_t i = 0; i < nDims; i++)
    7083             :             {
    7084           7 :                 if (bufferStride[i] != tmpBufferStrideVector[i])
    7085             :                 {
    7086           1 :                     bContiguous = false;
    7087           1 :                     break;
    7088             :                 }
    7089             :             }
    7090           4 :             if (bContiguous)
    7091             :             {
    7092             :                 // CPLDebug("GDAL", "GetMask(): contiguous case");
    7093           3 :                 memset(pDstBuffer, 1, nElts);
    7094           3 :                 return true;
    7095             :             }
    7096             :         }
    7097             : 
    7098             :         struct Stack
    7099             :         {
    7100             :             size_t nIters = 0;
    7101             :             GByte *dst_ptr = nullptr;
    7102             :             GPtrDiff_t dst_inc_offset = 0;
    7103             :         };
    7104             : 
    7105           4 :         std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
    7106           4 :         const size_t nBufferDTSize = bufferDataType.GetSize();
    7107          13 :         for (size_t i = 0; i < nDims; i++)
    7108             :         {
    7109           9 :             stack[i].dst_inc_offset =
    7110           9 :                 static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    7111             :         }
    7112           4 :         stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    7113             : 
    7114           4 :         size_t dimIdx = 0;
    7115           4 :         const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
    7116             :         GByte abyOne[16];  // 16 is sizeof GDT_CFloat64
    7117           4 :         CPLAssert(nBufferDTSize <= 16);
    7118           4 :         const GByte flag = 1;
    7119           4 :         GDALCopyWords64(&flag, GDT_UInt8, 0, abyOne,
    7120             :                         bufferDataType.GetNumericDataType(), 0, 1);
    7121             : 
    7122          28 :     lbl_next_depth:
    7123          28 :         if (dimIdx == nDimsMinus1)
    7124             :         {
    7125          19 :             auto nIters = nDims > 0 ? count[dimIdx] : 1;
    7126          19 :             GByte *dst_ptr = stack[dimIdx].dst_ptr;
    7127             : 
    7128             :             while (true)
    7129             :             {
    7130             :                 // cppcheck-suppress knownConditionTrueFalse
    7131          73 :                 if (bBufferDataTypeIsByte)
    7132             :                 {
    7133          24 :                     *dst_ptr = flag;
    7134             :                 }
    7135             :                 else
    7136             :                 {
    7137          49 :                     memcpy(dst_ptr, abyOne, nBufferDTSize);
    7138             :                 }
    7139             : 
    7140          73 :                 if ((--nIters) == 0)
    7141          19 :                     break;
    7142          54 :                 dst_ptr += stack[dimIdx].dst_inc_offset;
    7143             :             }
    7144             :         }
    7145             :         else
    7146             :         {
    7147           9 :             stack[dimIdx].nIters = count[dimIdx];
    7148             :             while (true)
    7149             :             {
    7150          24 :                 dimIdx++;
    7151          24 :                 stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    7152          24 :                 goto lbl_next_depth;
    7153          24 :             lbl_return_to_caller:
    7154          24 :                 dimIdx--;
    7155          24 :                 if ((--stack[dimIdx].nIters) == 0)
    7156           9 :                     break;
    7157          15 :                 stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    7158             :             }
    7159             :         }
    7160          28 :         if (dimIdx > 0)
    7161          24 :             goto lbl_return_to_caller;
    7162             : 
    7163           4 :         return true;
    7164             :     }
    7165             : 
    7166             :     const auto oTmpBufferDT =
    7167          44 :         GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
    7168             :             ? GDALExtendedDataType::Create(GDT_Float64)
    7169          88 :             : m_poParent->GetDataType();
    7170          44 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
    7171          44 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
    7172          44 :     if (!pTempBuffer)
    7173           0 :         return false;
    7174          88 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
    7175          44 :                           tmpBufferStrideVector.data(), oTmpBufferDT,
    7176             :                           pTempBuffer))
    7177             :     {
    7178           0 :         VSIFree(pTempBuffer);
    7179           0 :         return false;
    7180             :     }
    7181             : 
    7182          44 :     switch (oTmpBufferDT.GetNumericDataType())
    7183             :     {
    7184           7 :         case GDT_UInt8:
    7185           7 :             ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
    7186             :                                 pTempBuffer, oTmpBufferDT,
    7187             :                                 tmpBufferStrideVector);
    7188           7 :             break;
    7189             : 
    7190           0 :         case GDT_Int8:
    7191           0 :             ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
    7192             :                                 pTempBuffer, oTmpBufferDT,
    7193             :                                 tmpBufferStrideVector);
    7194           0 :             break;
    7195             : 
    7196           1 :         case GDT_UInt16:
    7197           1 :             ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
    7198             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
    7199             :                                   tmpBufferStrideVector);
    7200           1 :             break;
    7201             : 
    7202          14 :         case GDT_Int16:
    7203          14 :             ReadInternal<GInt16>(count, bufferStride, bufferDataType,
    7204             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7205             :                                  tmpBufferStrideVector);
    7206          14 :             break;
    7207             : 
    7208           1 :         case GDT_UInt32:
    7209           1 :             ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
    7210             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
    7211             :                                   tmpBufferStrideVector);
    7212           1 :             break;
    7213             : 
    7214           5 :         case GDT_Int32:
    7215           5 :             ReadInternal<GInt32>(count, bufferStride, bufferDataType,
    7216             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7217             :                                  tmpBufferStrideVector);
    7218           5 :             break;
    7219             : 
    7220           0 :         case GDT_UInt64:
    7221           0 :             ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
    7222             :                                         pDstBuffer, pTempBuffer, oTmpBufferDT,
    7223             :                                         tmpBufferStrideVector);
    7224           0 :             break;
    7225             : 
    7226           0 :         case GDT_Int64:
    7227           0 :             ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
    7228             :                                        pDstBuffer, pTempBuffer, oTmpBufferDT,
    7229             :                                        tmpBufferStrideVector);
    7230           0 :             break;
    7231             : 
    7232           0 :         case GDT_Float16:
    7233           0 :             ReadInternal<GFloat16>(count, bufferStride, bufferDataType,
    7234             :                                    pDstBuffer, pTempBuffer, oTmpBufferDT,
    7235             :                                    tmpBufferStrideVector);
    7236           0 :             break;
    7237             : 
    7238           7 :         case GDT_Float32:
    7239           7 :             ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
    7240             :                                 pTempBuffer, oTmpBufferDT,
    7241             :                                 tmpBufferStrideVector);
    7242           7 :             break;
    7243             : 
    7244           9 :         case GDT_Float64:
    7245           9 :             ReadInternal<double>(count, bufferStride, bufferDataType,
    7246             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7247             :                                  tmpBufferStrideVector);
    7248           9 :             break;
    7249           0 :         case GDT_Unknown:
    7250             :         case GDT_CInt16:
    7251             :         case GDT_CInt32:
    7252             :         case GDT_CFloat16:
    7253             :         case GDT_CFloat32:
    7254             :         case GDT_CFloat64:
    7255             :         case GDT_TypeCount:
    7256           0 :             CPLAssert(false);
    7257             :             break;
    7258             :     }
    7259             : 
    7260          44 :     VSIFree(pTempBuffer);
    7261             : 
    7262          44 :     return true;
    7263             : }
    7264             : 
    7265             : /************************************************************************/
    7266             : /*                          IsValidForDT()                              */
    7267             : /************************************************************************/
    7268             : 
    7269          40 : template <typename Type> static bool IsValidForDT(double dfVal)
    7270             : {
    7271          40 :     if (std::isnan(dfVal))
    7272           0 :         return false;
    7273          40 :     if (dfVal < static_cast<double>(cpl::NumericLimits<Type>::lowest()))
    7274           0 :         return false;
    7275          40 :     if (dfVal > static_cast<double>(cpl::NumericLimits<Type>::max()))
    7276           0 :         return false;
    7277          40 :     return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
    7278             : }
    7279             : 
    7280           9 : template <> bool IsValidForDT<double>(double)
    7281             : {
    7282           9 :     return true;
    7283             : }
    7284             : 
    7285             : /************************************************************************/
    7286             : /*                              IsNan()                                 */
    7287             : /************************************************************************/
    7288             : 
    7289        1438 : template <typename Type> inline bool IsNan(Type)
    7290             : {
    7291        1438 :     return false;
    7292             : }
    7293             : 
    7294          65 : template <> bool IsNan<double>(double val)
    7295             : {
    7296          65 :     return std::isnan(val);
    7297             : }
    7298             : 
    7299          26 : template <> bool IsNan<float>(float val)
    7300             : {
    7301          26 :     return std::isnan(val);
    7302             : }
    7303             : 
    7304             : /************************************************************************/
    7305             : /*                         ReadInternal()                               */
    7306             : /************************************************************************/
    7307             : 
    7308             : template <typename Type>
    7309          44 : void GDALMDArrayMask::ReadInternal(
    7310             :     const size_t *count, const GPtrDiff_t *bufferStride,
    7311             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    7312             :     const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
    7313             :     const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
    7314             : {
    7315          44 :     const size_t nDims = GetDimensionCount();
    7316             : 
    7317         220 :     const auto castValue = [](bool &bHasVal, double dfVal) -> Type
    7318             :     {
    7319         220 :         if (bHasVal)
    7320             :         {
    7321          49 :             if (IsValidForDT<Type>(dfVal))
    7322             :             {
    7323          49 :                 return static_cast<Type>(dfVal);
    7324             :             }
    7325             :             else
    7326             :             {
    7327           0 :                 bHasVal = false;
    7328             :             }
    7329             :         }
    7330         171 :         return 0;
    7331             :     };
    7332             : 
    7333          44 :     const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
    7334          44 :     bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
    7335             :     const Type nNoDataValue =
    7336          44 :         castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
    7337          44 :     bool bHasMissingValue = m_bHasMissingValue;
    7338          44 :     const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
    7339          44 :     bool bHasFillValue = m_bHasFillValue;
    7340          44 :     const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
    7341          44 :     bool bHasValidMin = m_bHasValidMin;
    7342          44 :     const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
    7343          44 :     bool bHasValidMax = m_bHasValidMax;
    7344          44 :     const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
    7345          44 :     const bool bHasValidFlags =
    7346          44 :         !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
    7347             : 
    7348         351 :     const auto IsValidFlag = [this](Type v)
    7349             :     {
    7350          54 :         if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
    7351             :         {
    7352          20 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
    7353             :             {
    7354          12 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
    7355             :                     m_anValidFlagValues[i])
    7356             :                 {
    7357           4 :                     return true;
    7358             :                 }
    7359             :             }
    7360             :         }
    7361          42 :         else if (!m_anValidFlagValues.empty())
    7362             :         {
    7363          49 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
    7364             :             {
    7365          29 :                 if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
    7366             :                 {
    7367           4 :                     return true;
    7368             :                 }
    7369             :             }
    7370             :         }
    7371             :         else /* if( !m_anValidFlagMasks.empty() ) */
    7372             :         {
    7373          31 :             for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
    7374             :             {
    7375          22 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
    7376             :                 {
    7377           9 :                     return true;
    7378             :                 }
    7379             :             }
    7380             :         }
    7381          37 :         return false;
    7382             :     };
    7383             : 
    7384             : #define GET_MASK_FOR_SAMPLE(v)                                                 \
    7385             :     static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
    7386             :                        !(bHasMissingValue && v == nMissingValue) &&            \
    7387             :                        !(bHasFillValue && v == nFillValue) &&                  \
    7388             :                        !(bHasValidMin && v < nValidMin) &&                     \
    7389             :                        !(bHasValidMax && v > nValidMax) &&                     \
    7390             :                        (!bHasValidFlags || IsValidFlag(v)));
    7391             : 
    7392          44 :     const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
    7393             :     /* Optimized case: Byte output and output buffer is contiguous */
    7394          44 :     if (bBufferDataTypeIsByte)
    7395             :     {
    7396          40 :         bool bContiguous = true;
    7397         103 :         for (size_t i = 0; i < nDims; i++)
    7398             :         {
    7399          64 :             if (bufferStride[i] != tmpBufferStrideVector[i])
    7400             :             {
    7401           1 :                 bContiguous = false;
    7402           1 :                 break;
    7403             :             }
    7404             :         }
    7405          40 :         if (bContiguous)
    7406             :         {
    7407          39 :             size_t nElts = 1;
    7408         102 :             for (size_t i = 0; i < nDims; i++)
    7409          63 :                 nElts *= count[i];
    7410             : 
    7411        1113 :             for (size_t i = 0; i < nElts; i++)
    7412             :             {
    7413        1074 :                 const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
    7414        1074 :                 static_cast<GByte *>(pDstBuffer)[i] =
    7415        1074 :                     GET_MASK_FOR_SAMPLE(*pSrc);
    7416             :             }
    7417          39 :             return;
    7418             :         }
    7419             :     }
    7420             : 
    7421           5 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
    7422             : 
    7423             :     struct Stack
    7424             :     {
    7425             :         size_t nIters = 0;
    7426             :         const GByte *src_ptr = nullptr;
    7427             :         GByte *dst_ptr = nullptr;
    7428             :         GPtrDiff_t src_inc_offset = 0;
    7429             :         GPtrDiff_t dst_inc_offset = 0;
    7430             :     };
    7431             : 
    7432          10 :     std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
    7433           5 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    7434          15 :     for (size_t i = 0; i < nDims; i++)
    7435             :     {
    7436          20 :         stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
    7437          10 :             tmpBufferStrideVector[i] * nTmpBufferDTSize);
    7438          10 :         stack[i].dst_inc_offset =
    7439          10 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    7440             :     }
    7441           5 :     stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
    7442           5 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    7443             : 
    7444           5 :     size_t dimIdx = 0;
    7445           5 :     const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
    7446             :     GByte abyZeroOrOne[2][16];  // 16 is sizeof GDT_CFloat64
    7447           5 :     CPLAssert(nBufferDTSize <= 16);
    7448          15 :     for (GByte flag = 0; flag <= 1; flag++)
    7449             :     {
    7450          10 :         GDALCopyWords64(&flag, m_dt.GetNumericDataType(), 0, abyZeroOrOne[flag],
    7451             :                         bufferDataType.GetNumericDataType(), 0, 1);
    7452             :     }
    7453             : 
    7454          43 : lbl_next_depth:
    7455          43 :     if (dimIdx == nDimsMinus1)
    7456             :     {
    7457          35 :         auto nIters = nDims > 0 ? count[dimIdx] : 1;
    7458          35 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
    7459          35 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
    7460             : 
    7461         420 :         while (true)
    7462             :         {
    7463         455 :             const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
    7464         455 :             const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
    7465             : 
    7466         455 :             if (bBufferDataTypeIsByte)
    7467             :             {
    7468          24 :                 *dst_ptr = flag;
    7469             :             }
    7470             :             else
    7471             :             {
    7472         431 :                 memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
    7473             :             }
    7474             : 
    7475         455 :             if ((--nIters) == 0)
    7476          35 :                 break;
    7477         420 :             src_ptr += stack[dimIdx].src_inc_offset;
    7478         420 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    7479             :         }
    7480             :     }
    7481             :     else
    7482             :     {
    7483           8 :         stack[dimIdx].nIters = count[dimIdx];
    7484             :         while (true)
    7485             :         {
    7486          38 :             dimIdx++;
    7487          38 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    7488          38 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    7489          38 :             goto lbl_next_depth;
    7490          38 :         lbl_return_to_caller:
    7491          38 :             dimIdx--;
    7492          38 :             if ((--stack[dimIdx].nIters) == 0)
    7493           8 :                 break;
    7494          30 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    7495          30 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    7496             :         }
    7497             :     }
    7498          43 :     if (dimIdx > 0)
    7499          38 :         goto lbl_return_to_caller;
    7500             : }
    7501             : 
    7502             : /************************************************************************/
    7503             : /*                            GetMask()                                 */
    7504             : /************************************************************************/
    7505             : 
    7506             : /** Return an array that is a mask for the current array
    7507             : 
    7508             :  This array will be of type Byte, with values set to 0 to indicate invalid
    7509             :  pixels of the current array, and values set to 1 to indicate valid pixels.
    7510             : 
    7511             :  The generic implementation honours the NoDataValue, as well as various
    7512             :  netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
    7513             :  and valid_range.
    7514             : 
    7515             :  Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
    7516             :  can be used to specify strings of the "flag_meanings" attribute
    7517             :  (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
    7518             :  for which pixels matching any of those flags will be set at 1 in the mask array,
    7519             :  and pixels matching none of those flags will be set at 0.
    7520             :  For example, let's consider the following netCDF variable defined with:
    7521             :  \verbatim
    7522             :  l2p_flags:valid_min = 0s ;
    7523             :  l2p_flags:valid_max = 256s ;
    7524             :  l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
    7525             :  l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
    7526             :  \endverbatim
    7527             : 
    7528             :  GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
    7529             :  - for pixel values *outside* valid_range [0,256], the mask value will be 0.
    7530             :  - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
    7531             :    will be 1.
    7532             :  - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
    7533             :    will be 0.
    7534             : 
    7535             :  This is the same as the C function GDALMDArrayGetMask().
    7536             : 
    7537             :  @param papszOptions NULL-terminated list of options, or NULL.
    7538             : 
    7539             :  @return a new array, that holds a reference to the original one, and thus is
    7540             :  a view of it (not a copy), or nullptr in case of error.
    7541             : */
    7542             : std::shared_ptr<GDALMDArray>
    7543          49 : GDALMDArray::GetMask(CSLConstList papszOptions) const
    7544             : {
    7545          98 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    7546          49 :     if (!self)
    7547             :     {
    7548           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    7549             :                  "Driver implementation issue: m_pSelf not set !");
    7550           0 :         return nullptr;
    7551             :     }
    7552          49 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    7553             :     {
    7554           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    7555             :                  "GetMask() only supports numeric data type");
    7556           1 :         return nullptr;
    7557             :     }
    7558          48 :     return GDALMDArrayMask::Create(self, papszOptions);
    7559             : }
    7560             : 
    7561             : /************************************************************************/
    7562             : /*                         IsRegularlySpaced()                          */
    7563             : /************************************************************************/
    7564             : 
    7565             : /** Returns whether an array is a 1D regularly spaced array.
    7566             :  *
    7567             :  * @param[out] dfStart     First value in the array
    7568             :  * @param[out] dfIncrement Increment/spacing between consecutive values.
    7569             :  * @return true if the array is regularly spaced.
    7570             :  */
    7571         335 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
    7572             : {
    7573         335 :     dfStart = 0;
    7574         335 :     dfIncrement = 0;
    7575         335 :     if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
    7576           0 :         return false;
    7577         335 :     const auto nSize = GetDimensions()[0]->GetSize();
    7578         335 :     if (nSize <= 1 || nSize > 10 * 1000 * 1000)
    7579           2 :         return false;
    7580             : 
    7581         333 :     size_t nCount = static_cast<size_t>(nSize);
    7582         666 :     std::vector<double> adfTmp;
    7583             :     try
    7584             :     {
    7585         333 :         adfTmp.resize(nCount);
    7586             :     }
    7587           0 :     catch (const std::exception &)
    7588             :     {
    7589           0 :         return false;
    7590             :     }
    7591             : 
    7592         333 :     GUInt64 anStart[1] = {0};
    7593         333 :     size_t anCount[1] = {nCount};
    7594             : 
    7595             :     const auto IsRegularlySpacedInternal =
    7596       90468 :         [&dfStart, &dfIncrement, &anCount, &adfTmp]()
    7597             :     {
    7598         443 :         dfStart = adfTmp[0];
    7599         443 :         dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
    7600         443 :         if (dfIncrement == 0)
    7601             :         {
    7602           3 :             return false;
    7603             :         }
    7604       22495 :         for (size_t i = 1; i < anCount[0]; i++)
    7605             :         {
    7606       22067 :             if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
    7607       22067 :                 1e-3 * fabs(dfIncrement))
    7608             :             {
    7609          12 :                 return false;
    7610             :             }
    7611             :         }
    7612         428 :         return true;
    7613         333 :     };
    7614             : 
    7615             :     // First try with the first block(s). This can avoid excessive processing
    7616             :     // time, for example with Zarr datasets.
    7617             :     // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
    7618             :     // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
    7619         333 :     const auto nBlockSize = GetBlockSize()[0];
    7620         333 :     if (nCount >= 5 && nBlockSize <= nCount / 2)
    7621             :     {
    7622             :         size_t nReducedCount =
    7623         113 :             std::max<size_t>(3, static_cast<size_t>(nBlockSize));
    7624         546 :         while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
    7625         433 :             nReducedCount *= 2;
    7626         113 :         anCount[0] = nReducedCount;
    7627         113 :         if (!Read(anStart, anCount, nullptr, nullptr,
    7628         226 :                   GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
    7629             :         {
    7630           0 :             return false;
    7631             :         }
    7632         113 :         if (!IsRegularlySpacedInternal())
    7633             :         {
    7634           3 :             return false;
    7635             :         }
    7636             : 
    7637             :         // Get next values
    7638         110 :         anStart[0] = nReducedCount;
    7639         110 :         anCount[0] = nCount - nReducedCount;
    7640             :     }
    7641             : 
    7642         330 :     if (!Read(anStart, anCount, nullptr, nullptr,
    7643         660 :               GDALExtendedDataType::Create(GDT_Float64),
    7644         330 :               &adfTmp[static_cast<size_t>(anStart[0])]))
    7645             :     {
    7646           0 :         return false;
    7647             :     }
    7648             : 
    7649         330 :     return IsRegularlySpacedInternal();
    7650             : }
    7651             : 
    7652             : /************************************************************************/
    7653             : /*                         GuessGeoTransform()                          */
    7654             : /************************************************************************/
    7655             : 
    7656             : /** Returns whether 2 specified dimensions form a geotransform
    7657             :  *
    7658             :  * @param nDimX                Index of the X axis.
    7659             :  * @param nDimY                Index of the Y axis.
    7660             :  * @param bPixelIsPoint        Whether the geotransform should be returned
    7661             :  *                             with the pixel-is-point (pixel-center) convention
    7662             :  *                             (bPixelIsPoint = true), or with the pixel-is-area
    7663             :  *                             (top left corner convention)
    7664             :  *                             (bPixelIsPoint = false)
    7665             :  * @param[out] gt              Computed geotransform
    7666             :  * @return true if a geotransform could be computed.
    7667             :  */
    7668         266 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
    7669             :                                     bool bPixelIsPoint,
    7670             :                                     GDALGeoTransform &gt) const
    7671             : {
    7672         266 :     const auto &dims(GetDimensions());
    7673         532 :     auto poVarX = dims[nDimX]->GetIndexingVariable();
    7674         532 :     auto poVarY = dims[nDimY]->GetIndexingVariable();
    7675         266 :     double dfXStart = 0.0;
    7676         266 :     double dfXSpacing = 0.0;
    7677         266 :     double dfYStart = 0.0;
    7678         266 :     double dfYSpacing = 0.0;
    7679         590 :     if (poVarX && poVarX->GetDimensionCount() == 1 &&
    7680         324 :         poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
    7681         464 :         poVarY && poVarY->GetDimensionCount() == 1 &&
    7682         151 :         poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
    7683         574 :         poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
    7684         146 :         poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
    7685             :     {
    7686         146 :         gt[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
    7687         146 :         gt[1] = dfXSpacing;
    7688         146 :         gt[2] = 0;
    7689         146 :         gt[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
    7690         146 :         gt[4] = 0;
    7691         146 :         gt[5] = dfYSpacing;
    7692         146 :         return true;
    7693             :     }
    7694         120 :     return false;
    7695             : }
    7696             : 
    7697             : /** Returns whether 2 specified dimensions form a geotransform
    7698             :  *
    7699             :  * @param nDimX                Index of the X axis.
    7700             :  * @param nDimY                Index of the Y axis.
    7701             :  * @param bPixelIsPoint        Whether the geotransform should be returned
    7702             :  *                             with the pixel-is-point (pixel-center) convention
    7703             :  *                             (bPixelIsPoint = true), or with the pixel-is-area
    7704             :  *                             (top left corner convention)
    7705             :  *                             (bPixelIsPoint = false)
    7706             :  * @param[out] adfGeoTransform Computed geotransform
    7707             :  * @return true if a geotransform could be computed.
    7708             :  */
    7709           0 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
    7710             :                                     bool bPixelIsPoint,
    7711             :                                     double adfGeoTransform[6]) const
    7712             : {
    7713           0 :     GDALGeoTransform *gt =
    7714             :         reinterpret_cast<GDALGeoTransform *>(adfGeoTransform);
    7715           0 :     return GuessGeoTransform(nDimX, nDimY, bPixelIsPoint, *gt);
    7716             : }
    7717             : 
    7718             : /************************************************************************/
    7719             : /*                       GDALMDArrayResampled                           */
    7720             : /************************************************************************/
    7721             : 
    7722             : class GDALMDArrayResampledDataset;
    7723             : 
    7724             : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
    7725             : {
    7726             :   protected:
    7727             :     CPLErr IReadBlock(int, int, void *) override;
    7728             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
    7729             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
    7730             :                      GDALDataType eBufType, GSpacing nPixelSpaceBuf,
    7731             :                      GSpacing nLineSpaceBuf,
    7732             :                      GDALRasterIOExtraArg *psExtraArg) override;
    7733             : 
    7734             :   public:
    7735             :     explicit GDALMDArrayResampledDatasetRasterBand(
    7736             :         GDALMDArrayResampledDataset *poDSIn);
    7737             : 
    7738             :     double GetNoDataValue(int *pbHasNoData) override;
    7739             : };
    7740             : 
    7741             : class GDALMDArrayResampledDataset final : public GDALPamDataset
    7742             : {
    7743             :     friend class GDALMDArrayResampled;
    7744             :     friend class GDALMDArrayResampledDatasetRasterBand;
    7745             : 
    7746             :     std::shared_ptr<GDALMDArray> m_poArray;
    7747             :     const size_t m_iXDim;
    7748             :     const size_t m_iYDim;
    7749             :     GDALGeoTransform m_gt{};
    7750             :     bool m_bHasGT = false;
    7751             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
    7752             : 
    7753             :     std::vector<GUInt64> m_anOffset{};
    7754             :     std::vector<size_t> m_anCount{};
    7755             :     std::vector<GPtrDiff_t> m_anStride{};
    7756             : 
    7757             :     std::string m_osFilenameLong{};
    7758             :     std::string m_osFilenameLat{};
    7759             : 
    7760             :   public:
    7761          24 :     GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
    7762             :                                 size_t iXDim, size_t iYDim)
    7763          24 :         : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
    7764          24 :           m_anOffset(m_poArray->GetDimensionCount(), 0),
    7765          24 :           m_anCount(m_poArray->GetDimensionCount(), 1),
    7766          72 :           m_anStride(m_poArray->GetDimensionCount(), 0)
    7767             :     {
    7768          24 :         const auto &dims(m_poArray->GetDimensions());
    7769             : 
    7770          24 :         nRasterYSize = static_cast<int>(
    7771          24 :             std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
    7772          24 :         nRasterXSize = static_cast<int>(
    7773          24 :             std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
    7774             : 
    7775          24 :         m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false, m_gt);
    7776             : 
    7777          24 :         SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
    7778          24 :     }
    7779             : 
    7780             :     ~GDALMDArrayResampledDataset() override;
    7781             : 
    7782          43 :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override
    7783             :     {
    7784          43 :         gt = m_gt;
    7785          43 :         return m_bHasGT ? CE_None : CE_Failure;
    7786             :     }
    7787             : 
    7788         105 :     const OGRSpatialReference *GetSpatialRef() const override
    7789             :     {
    7790         105 :         m_poSRS = m_poArray->GetSpatialRef();
    7791         105 :         if (m_poSRS)
    7792             :         {
    7793          79 :             m_poSRS.reset(m_poSRS->Clone());
    7794         158 :             auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
    7795         237 :             for (auto &m : axisMapping)
    7796             :             {
    7797         158 :                 if (m == static_cast<int>(m_iXDim) + 1)
    7798          79 :                     m = 1;
    7799          79 :                 else if (m == static_cast<int>(m_iYDim) + 1)
    7800          79 :                     m = 2;
    7801             :             }
    7802          79 :             m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
    7803             :         }
    7804         105 :         return m_poSRS.get();
    7805             :     }
    7806             : 
    7807           5 :     void SetGeolocationArray(const std::string &osFilenameLong,
    7808             :                              const std::string &osFilenameLat)
    7809             :     {
    7810           5 :         m_osFilenameLong = osFilenameLong;
    7811           5 :         m_osFilenameLat = osFilenameLat;
    7812          10 :         CPLStringList aosGeoLoc;
    7813           5 :         aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
    7814           5 :         aosGeoLoc.SetNameValue("LINE_STEP", "1");
    7815           5 :         aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
    7816           5 :         aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
    7817           5 :         aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG);  // FIXME?
    7818           5 :         aosGeoLoc.SetNameValue("X_BAND", "1");
    7819           5 :         aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
    7820           5 :         aosGeoLoc.SetNameValue("Y_BAND", "1");
    7821           5 :         aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
    7822           5 :         aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
    7823           5 :         SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
    7824           5 :     }
    7825             : };
    7826             : 
    7827          48 : GDALMDArrayResampledDataset::~GDALMDArrayResampledDataset()
    7828             : {
    7829          24 :     if (!m_osFilenameLong.empty())
    7830           5 :         VSIUnlink(m_osFilenameLong.c_str());
    7831          24 :     if (!m_osFilenameLat.empty())
    7832           5 :         VSIUnlink(m_osFilenameLat.c_str());
    7833          48 : }
    7834             : 
    7835             : /************************************************************************/
    7836             : /*                   GDALMDArrayResampledDatasetRasterBand()            */
    7837             : /************************************************************************/
    7838             : 
    7839          24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
    7840          24 :     GDALMDArrayResampledDataset *poDSIn)
    7841             : {
    7842          24 :     const auto &poArray(poDSIn->m_poArray);
    7843          24 :     const auto blockSize(poArray->GetBlockSize());
    7844          24 :     nBlockYSize = (blockSize[poDSIn->m_iYDim])
    7845          24 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    7846          13 :                                                   blockSize[poDSIn->m_iYDim]))
    7847          24 :                       : 1;
    7848          24 :     nBlockXSize = blockSize[poDSIn->m_iXDim]
    7849          13 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    7850          13 :                                                   blockSize[poDSIn->m_iXDim]))
    7851          24 :                       : poDSIn->GetRasterXSize();
    7852          24 :     eDataType = poArray->GetDataType().GetNumericDataType();
    7853          24 :     eAccess = poDSIn->eAccess;
    7854          24 : }
    7855             : 
    7856             : /************************************************************************/
    7857             : /*                           GetNoDataValue()                           */
    7858             : /************************************************************************/
    7859             : 
    7860          54 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
    7861             : {
    7862          54 :     auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
    7863          54 :     const auto &poArray(l_poDS->m_poArray);
    7864          54 :     bool bHasNodata = false;
    7865          54 :     double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
    7866          54 :     if (pbHasNoData)
    7867          48 :         *pbHasNoData = bHasNodata;
    7868          54 :     return dfRes;
    7869             : }
    7870             : 
    7871             : /************************************************************************/
    7872             : /*                            IReadBlock()                              */
    7873             : /************************************************************************/
    7874             : 
    7875           0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
    7876             :                                                          int nBlockYOff,
    7877             :                                                          void *pImage)
    7878             : {
    7879           0 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    7880           0 :     const int nXOff = nBlockXOff * nBlockXSize;
    7881           0 :     const int nYOff = nBlockYOff * nBlockYSize;
    7882           0 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    7883           0 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    7884             :     GDALRasterIOExtraArg sExtraArg;
    7885           0 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    7886           0 :     return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    7887             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    7888           0 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    7889             : }
    7890             : 
    7891             : /************************************************************************/
    7892             : /*                            IRasterIO()                               */
    7893             : /************************************************************************/
    7894             : 
    7895          32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
    7896             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
    7897             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
    7898             :     GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
    7899             :     GDALRasterIOExtraArg *psExtraArg)
    7900             : {
    7901          32 :     auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
    7902          32 :     const auto &poArray(l_poDS->m_poArray);
    7903          32 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
    7904          32 :     if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
    7905          32 :         nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
    7906          32 :         (nLineSpaceBuf % nBufferDTSize) == 0)
    7907             :     {
    7908          32 :         l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
    7909          32 :         l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
    7910          64 :         l_poDS->m_anStride[l_poDS->m_iXDim] =
    7911          32 :             static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
    7912             : 
    7913          32 :         l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
    7914          32 :         l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
    7915          64 :         l_poDS->m_anStride[l_poDS->m_iYDim] =
    7916          32 :             static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
    7917             : 
    7918          64 :         return poArray->Read(l_poDS->m_anOffset.data(),
    7919          32 :                              l_poDS->m_anCount.data(), nullptr,
    7920          32 :                              l_poDS->m_anStride.data(),
    7921          64 :                              GDALExtendedDataType::Create(eBufType), pData)
    7922          32 :                    ? CE_None
    7923          32 :                    : CE_Failure;
    7924             :     }
    7925           0 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    7926             :                                      pData, nBufXSize, nBufYSize, eBufType,
    7927           0 :                                      nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
    7928             : }
    7929             : 
    7930             : class GDALMDArrayResampled final : public GDALPamMDArray
    7931             : {
    7932             :   private:
    7933             :     std::shared_ptr<GDALMDArray> m_poParent{};
    7934             :     std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
    7935             :     std::vector<GUInt64> m_anBlockSize;
    7936             :     GDALExtendedDataType m_dt;
    7937             :     std::shared_ptr<OGRSpatialReference> m_poSRS{};
    7938             :     std::shared_ptr<GDALMDArray> m_poVarX{};
    7939             :     std::shared_ptr<GDALMDArray> m_poVarY{};
    7940             :     std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
    7941             :     std::unique_ptr<GDALDataset> m_poReprojectedDS{};
    7942             : 
    7943             :   protected:
    7944          21 :     GDALMDArrayResampled(
    7945             :         const std::shared_ptr<GDALMDArray> &poParent,
    7946             :         const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
    7947             :         const std::vector<GUInt64> &anBlockSize)
    7948          42 :         : GDALAbstractMDArray(std::string(),
    7949          42 :                               "Resampled view of " + poParent->GetFullName()),
    7950             :           GDALPamMDArray(
    7951          42 :               std::string(), "Resampled view of " + poParent->GetFullName(),
    7952          42 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
    7953          21 :           m_poParent(std::move(poParent)), m_apoDims(apoDims),
    7954         105 :           m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
    7955             :     {
    7956          21 :         CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
    7957          21 :         CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
    7958          21 :     }
    7959             : 
    7960             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    7961             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    7962             :                const GDALExtendedDataType &bufferDataType,
    7963             :                void *pDstBuffer) const override;
    7964             : 
    7965             :   public:
    7966             :     static std::shared_ptr<GDALMDArray>
    7967             :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    7968             :            const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
    7969             :            GDALRIOResampleAlg resampleAlg,
    7970             :            const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
    7971             : 
    7972          42 :     ~GDALMDArrayResampled() override
    7973          21 :     {
    7974             :         // First close the warped VRT
    7975          21 :         m_poReprojectedDS.reset();
    7976          21 :         m_poParentDS.reset();
    7977          42 :     }
    7978             : 
    7979          11 :     bool IsWritable() const override
    7980             :     {
    7981          11 :         return false;
    7982             :     }
    7983             : 
    7984          74 :     const std::string &GetFilename() const override
    7985             :     {
    7986          74 :         return m_poParent->GetFilename();
    7987             :     }
    7988             : 
    7989             :     const std::vector<std::shared_ptr<GDALDimension>> &
    7990         257 :     GetDimensions() const override
    7991             :     {
    7992         257 :         return m_apoDims;
    7993             :     }
    7994             : 
    7995         109 :     const GDALExtendedDataType &GetDataType() const override
    7996             :     {
    7997         109 :         return m_dt;
    7998             :     }
    7999             : 
    8000          21 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    8001             :     {
    8002          21 :         return m_poSRS;
    8003             :     }
    8004             : 
    8005          12 :     std::vector<GUInt64> GetBlockSize() const override
    8006             :     {
    8007          12 :         return m_anBlockSize;
    8008             :     }
    8009             : 
    8010             :     std::shared_ptr<GDALAttribute>
    8011           1 :     GetAttribute(const std::string &osName) const override
    8012             :     {
    8013           1 :         return m_poParent->GetAttribute(osName);
    8014             :     }
    8015             : 
    8016             :     std::vector<std::shared_ptr<GDALAttribute>>
    8017          12 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    8018             :     {
    8019          12 :         return m_poParent->GetAttributes(papszOptions);
    8020             :     }
    8021             : 
    8022           1 :     const std::string &GetUnit() const override
    8023             :     {
    8024           1 :         return m_poParent->GetUnit();
    8025             :     }
    8026             : 
    8027           1 :     const void *GetRawNoDataValue() const override
    8028             :     {
    8029           1 :         return m_poParent->GetRawNoDataValue();
    8030             :     }
    8031             : 
    8032           1 :     double GetOffset(bool *pbHasOffset,
    8033             :                      GDALDataType *peStorageType) const override
    8034             :     {
    8035           1 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    8036             :     }
    8037             : 
    8038           1 :     double GetScale(bool *pbHasScale,
    8039             :                     GDALDataType *peStorageType) const override
    8040             :     {
    8041           1 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    8042             :     }
    8043             : };
    8044             : 
    8045             : /************************************************************************/
    8046             : /*                   GDALMDArrayResampled::Create()                     */
    8047             : /************************************************************************/
    8048             : 
    8049          29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
    8050             :     const std::shared_ptr<GDALMDArray> &poParent,
    8051             :     const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
    8052             :     GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
    8053             :     CSLConstList /* papszOptions */)
    8054             : {
    8055          29 :     const char *pszResampleAlg = "nearest";
    8056          29 :     bool unsupported = false;
    8057          29 :     switch (resampleAlg)
    8058             :     {
    8059          16 :         case GRIORA_NearestNeighbour:
    8060          16 :             pszResampleAlg = "nearest";
    8061          16 :             break;
    8062           2 :         case GRIORA_Bilinear:
    8063           2 :             pszResampleAlg = "bilinear";
    8064           2 :             break;
    8065           5 :         case GRIORA_Cubic:
    8066           5 :             pszResampleAlg = "cubic";
    8067           5 :             break;
    8068           1 :         case GRIORA_CubicSpline:
    8069           1 :             pszResampleAlg = "cubicspline";
    8070           1 :             break;
    8071           1 :         case GRIORA_Lanczos:
    8072           1 :             pszResampleAlg = "lanczos";
    8073           1 :             break;
    8074           1 :         case GRIORA_Average:
    8075           1 :             pszResampleAlg = "average";
    8076           1 :             break;
    8077           1 :         case GRIORA_Mode:
    8078           1 :             pszResampleAlg = "mode";
    8079           1 :             break;
    8080           1 :         case GRIORA_Gauss:
    8081           1 :             unsupported = true;
    8082           1 :             break;
    8083           0 :         case GRIORA_RESERVED_START:
    8084           0 :             unsupported = true;
    8085           0 :             break;
    8086           0 :         case GRIORA_RESERVED_END:
    8087           0 :             unsupported = true;
    8088           0 :             break;
    8089           1 :         case GRIORA_RMS:
    8090           1 :             pszResampleAlg = "rms";
    8091           1 :             break;
    8092             :     }
    8093          29 :     if (unsupported)
    8094             :     {
    8095           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    8096             :                  "Unsupported resample method for GetResampled()");
    8097           1 :         return nullptr;
    8098             :     }
    8099             : 
    8100          28 :     if (poParent->GetDimensionCount() < 2)
    8101             :     {
    8102           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    8103             :                  "GetResampled() only supports 2 dimensions or more");
    8104           1 :         return nullptr;
    8105             :     }
    8106             : 
    8107          27 :     const auto &aoParentDims = poParent->GetDimensions();
    8108          27 :     if (apoNewDimsIn.size() != aoParentDims.size())
    8109             :     {
    8110           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8111             :                  "GetResampled(): apoNewDims size should be the same as "
    8112             :                  "GetDimensionCount()");
    8113           2 :         return nullptr;
    8114             :     }
    8115             : 
    8116          50 :     std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
    8117          25 :     apoNewDims.reserve(apoNewDimsIn.size());
    8118             : 
    8119          50 :     std::vector<GUInt64> anBlockSize;
    8120          25 :     anBlockSize.reserve(apoNewDimsIn.size());
    8121          50 :     const auto &anParentBlockSize = poParent->GetBlockSize();
    8122             : 
    8123          50 :     auto apoParentDims = poParent->GetDimensions();
    8124             :     // Special case for NASA EMIT datasets
    8125          30 :     const bool bYXBandOrder = (apoParentDims.size() == 3 &&
    8126           7 :                                apoParentDims[0]->GetName() == "downtrack" &&
    8127          32 :                                apoParentDims[1]->GetName() == "crosstrack" &&
    8128           2 :                                apoParentDims[2]->GetName() == "bands");
    8129             : 
    8130             :     const size_t iYDimParent =
    8131          25 :         bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
    8132             :     const size_t iXDimParent =
    8133          25 :         bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
    8134             : 
    8135          77 :     for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
    8136             :     {
    8137          53 :         if (i == iYDimParent || i == iXDimParent)
    8138          48 :             continue;
    8139           5 :         if (apoNewDimsIn[i] == nullptr)
    8140             :         {
    8141           3 :             apoNewDims.emplace_back(aoParentDims[i]);
    8142             :         }
    8143           3 :         else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
    8144           1 :                  apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
    8145             :         {
    8146           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    8147             :                      "GetResampled(): apoNewDims[%u] should be the same "
    8148             :                      "as its parent",
    8149             :                      i);
    8150           1 :             return nullptr;
    8151             :         }
    8152             :         else
    8153             :         {
    8154           1 :             apoNewDims.emplace_back(aoParentDims[i]);
    8155             :         }
    8156           4 :         anBlockSize.emplace_back(anParentBlockSize[i]);
    8157             :     }
    8158             : 
    8159             :     std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
    8160          48 :         new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
    8161             : 
    8162          24 :     double dfXStart = 0.0;
    8163          24 :     double dfXSpacing = 0.0;
    8164          24 :     bool gotXSpacing = false;
    8165          48 :     auto poNewDimX = apoNewDimsIn[iXDimParent];
    8166          24 :     if (poNewDimX)
    8167             :     {
    8168           4 :         if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
    8169             :         {
    8170           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    8171             :                      "Too big size for X dimension");
    8172           0 :             return nullptr;
    8173             :         }
    8174           4 :         auto var = poNewDimX->GetIndexingVariable();
    8175           4 :         if (var)
    8176             :         {
    8177           2 :             if (var->GetDimensionCount() != 1 ||
    8178           2 :                 var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
    8179           1 :                 !var->IsRegularlySpaced(dfXStart, dfXSpacing))
    8180             :             {
    8181           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    8182             :                          "New X dimension should be indexed by a regularly "
    8183             :                          "spaced variable");
    8184           0 :                 return nullptr;
    8185             :             }
    8186           1 :             gotXSpacing = true;
    8187             :         }
    8188             :     }
    8189             : 
    8190          24 :     double dfYStart = 0.0;
    8191          24 :     double dfYSpacing = 0.0;
    8192          48 :     auto poNewDimY = apoNewDimsIn[iYDimParent];
    8193          24 :     bool gotYSpacing = false;
    8194          24 :     if (poNewDimY)
    8195             :     {
    8196           4 :         if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
    8197             :         {
    8198           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    8199             :                      "Too big size for Y dimension");
    8200           0 :             return nullptr;
    8201             :         }
    8202           4 :         auto var = poNewDimY->GetIndexingVariable();
    8203           4 :         if (var)
    8204             :         {
    8205           2 :             if (var->GetDimensionCount() != 1 ||
    8206           2 :                 var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
    8207           1 :                 !var->IsRegularlySpaced(dfYStart, dfYSpacing))
    8208             :             {
    8209           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    8210             :                          "New Y dimension should be indexed by a regularly "
    8211             :                          "spaced variable");
    8212           0 :                 return nullptr;
    8213             :             }
    8214           1 :             gotYSpacing = true;
    8215             :         }
    8216             :     }
    8217             : 
    8218             :     // This limitation could probably be removed
    8219          24 :     if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
    8220             :     {
    8221           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    8222             :                  "Either none of new X or Y dimension should have an indexing "
    8223             :                  "variable, or both should both should have one.");
    8224           0 :         return nullptr;
    8225             :     }
    8226             : 
    8227          48 :     std::string osDstWKT;
    8228          24 :     if (poTargetSRS)
    8229             :     {
    8230           2 :         char *pszDstWKT = nullptr;
    8231           2 :         if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
    8232             :         {
    8233           0 :             CPLFree(pszDstWKT);
    8234           0 :             return nullptr;
    8235             :         }
    8236           2 :         osDstWKT = pszDstWKT;
    8237           2 :         CPLFree(pszDstWKT);
    8238             :     }
    8239             : 
    8240             :     // Use coordinate variables for geolocation array
    8241          48 :     const auto apoCoordinateVars = poParent->GetCoordinateVariables();
    8242          24 :     bool useGeolocationArray = false;
    8243          24 :     if (apoCoordinateVars.size() >= 2)
    8244             :     {
    8245           0 :         std::shared_ptr<GDALMDArray> poLongVar;
    8246           0 :         std::shared_ptr<GDALMDArray> poLatVar;
    8247          15 :         for (const auto &poCoordVar : apoCoordinateVars)
    8248             :         {
    8249          10 :             const auto &osName = poCoordVar->GetName();
    8250          30 :             const auto poAttr = poCoordVar->GetAttribute("standard_name");
    8251          20 :             std::string osStandardName;
    8252          12 :             if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
    8253           2 :                 poAttr->GetDimensionCount() == 0)
    8254             :             {
    8255           2 :                 const char *pszStandardName = poAttr->ReadAsString();
    8256           2 :                 if (pszStandardName)
    8257           2 :                     osStandardName = pszStandardName;
    8258             :             }
    8259          21 :             if (osName == "lon" || osName == "longitude" ||
    8260          21 :                 osName == "Longitude" || osStandardName == "longitude")
    8261             :             {
    8262           5 :                 poLongVar = poCoordVar;
    8263             :             }
    8264           6 :             else if (osName == "lat" || osName == "latitude" ||
    8265           6 :                      osName == "Latitude" || osStandardName == "latitude")
    8266             :             {
    8267           5 :                 poLatVar = poCoordVar;
    8268             :             }
    8269             :         }
    8270           5 :         if (poLatVar != nullptr && poLongVar != nullptr)
    8271             :         {
    8272           5 :             const auto longDimCount = poLongVar->GetDimensionCount();
    8273           5 :             const auto &longDims = poLongVar->GetDimensions();
    8274           5 :             const auto latDimCount = poLatVar->GetDimensionCount();
    8275           5 :             const auto &latDims = poLatVar->GetDimensions();
    8276           5 :             const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
    8277           5 :             const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
    8278           0 :             if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
    8279           5 :                 latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
    8280             :             {
    8281             :                 // Geolocation arrays are 1D, and of consistent size with
    8282             :                 // the variable
    8283           0 :                 useGeolocationArray = true;
    8284             :             }
    8285           1 :             else if ((longDimCount == 2 ||
    8286           6 :                       (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
    8287          10 :                      longDims[longDimCount - 2]->GetSize() == yDimSize &&
    8288          10 :                      longDims[longDimCount - 1]->GetSize() == xDimSize &&
    8289           1 :                      (latDimCount == 2 ||
    8290           6 :                       (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
    8291          15 :                      latDims[latDimCount - 2]->GetSize() == yDimSize &&
    8292           5 :                      latDims[latDimCount - 1]->GetSize() == xDimSize)
    8293             : 
    8294             :             {
    8295             :                 // Geolocation arrays are 2D (or 3D with first dimension of
    8296             :                 // size 1, as found in Sentinel 5P products), and of consistent
    8297             :                 // size with the variable
    8298           5 :                 useGeolocationArray = true;
    8299             :             }
    8300             :             else
    8301             :             {
    8302           0 :                 CPLDebug(
    8303             :                     "GDAL",
    8304             :                     "Longitude and latitude coordinate variables found, "
    8305             :                     "but their characteristics are not compatible of using "
    8306             :                     "them as geolocation arrays");
    8307             :             }
    8308           5 :             if (useGeolocationArray)
    8309             :             {
    8310          10 :                 CPLDebug("GDAL",
    8311             :                          "Setting geolocation array from variables %s and %s",
    8312           5 :                          poLongVar->GetName().c_str(),
    8313           5 :                          poLatVar->GetName().c_str());
    8314             :                 const std::string osFilenameLong =
    8315           5 :                     VSIMemGenerateHiddenFilename("longitude.tif");
    8316             :                 const std::string osFilenameLat =
    8317           5 :                     VSIMemGenerateHiddenFilename("latitude.tif");
    8318             :                 std::unique_ptr<GDALDataset> poTmpLongDS(
    8319             :                     longDimCount == 1
    8320           0 :                         ? poLongVar->AsClassicDataset(0, 0)
    8321          20 :                         : poLongVar->AsClassicDataset(longDimCount - 1,
    8322          15 :                                                       longDimCount - 2));
    8323           5 :                 auto hTIFFLongDS = GDALTranslate(
    8324             :                     osFilenameLong.c_str(),
    8325             :                     GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
    8326             :                 std::unique_ptr<GDALDataset> poTmpLatDS(
    8327           0 :                     latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
    8328          20 :                                      : poLatVar->AsClassicDataset(
    8329          15 :                                            latDimCount - 1, latDimCount - 2));
    8330           5 :                 auto hTIFFLatDS = GDALTranslate(
    8331             :                     osFilenameLat.c_str(),
    8332             :                     GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
    8333           5 :                 const bool bError =
    8334           5 :                     (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
    8335           5 :                 GDALClose(hTIFFLongDS);
    8336           5 :                 GDALClose(hTIFFLatDS);
    8337           5 :                 if (bError)
    8338             :                 {
    8339           0 :                     VSIUnlink(osFilenameLong.c_str());
    8340           0 :                     VSIUnlink(osFilenameLat.c_str());
    8341           0 :                     return nullptr;
    8342             :                 }
    8343             : 
    8344           5 :                 poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
    8345             :             }
    8346             :         }
    8347             :         else
    8348             :         {
    8349           0 :             CPLDebug("GDAL",
    8350             :                      "Coordinate variables available for %s, but "
    8351             :                      "longitude and/or latitude variables were not identified",
    8352           0 :                      poParent->GetName().c_str());
    8353             :         }
    8354             :     }
    8355             : 
    8356             :     // Build gdalwarp arguments
    8357          48 :     CPLStringList aosArgv;
    8358             : 
    8359          24 :     aosArgv.AddString("-of");
    8360          24 :     aosArgv.AddString("VRT");
    8361             : 
    8362          24 :     aosArgv.AddString("-r");
    8363          24 :     aosArgv.AddString(pszResampleAlg);
    8364             : 
    8365          24 :     if (!osDstWKT.empty())
    8366             :     {
    8367           2 :         aosArgv.AddString("-t_srs");
    8368           2 :         aosArgv.AddString(osDstWKT.c_str());
    8369             :     }
    8370             : 
    8371          24 :     if (useGeolocationArray)
    8372           5 :         aosArgv.AddString("-geoloc");
    8373             : 
    8374          24 :     if (gotXSpacing && gotYSpacing)
    8375             :     {
    8376           1 :         const double dfXMin = dfXStart - dfXSpacing / 2;
    8377             :         const double dfXMax =
    8378           1 :             dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
    8379           1 :         const double dfYMax = dfYStart - dfYSpacing / 2;
    8380             :         const double dfYMin =
    8381           1 :             dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
    8382           1 :         aosArgv.AddString("-te");
    8383           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
    8384           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
    8385           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
    8386           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
    8387             :     }
    8388             : 
    8389          24 :     if (poNewDimX && poNewDimY)
    8390             :     {
    8391           3 :         aosArgv.AddString("-ts");
    8392             :         aosArgv.AddString(
    8393           3 :             CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
    8394             :         aosArgv.AddString(
    8395           3 :             CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
    8396             :     }
    8397          21 :     else if (poNewDimX)
    8398             :     {
    8399           1 :         aosArgv.AddString("-ts");
    8400             :         aosArgv.AddString(
    8401           1 :             CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
    8402           1 :         aosArgv.AddString("0");
    8403             :     }
    8404          20 :     else if (poNewDimY)
    8405             :     {
    8406           1 :         aosArgv.AddString("-ts");
    8407           1 :         aosArgv.AddString("0");
    8408             :         aosArgv.AddString(
    8409           1 :             CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
    8410             :     }
    8411             : 
    8412             :     // Create a warped VRT dataset
    8413             :     GDALWarpAppOptions *psOptions =
    8414          24 :         GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
    8415          24 :     GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
    8416             :     std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
    8417          48 :         GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
    8418          24 :     GDALWarpAppOptionsFree(psOptions);
    8419          24 :     if (poReprojectedDS == nullptr)
    8420           3 :         return nullptr;
    8421             : 
    8422             :     int nBlockXSize;
    8423             :     int nBlockYSize;
    8424          21 :     poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    8425          21 :     anBlockSize.emplace_back(nBlockYSize);
    8426          21 :     anBlockSize.emplace_back(nBlockXSize);
    8427             : 
    8428          21 :     GDALGeoTransform gt;
    8429          21 :     CPLErr eErr = poReprojectedDS->GetGeoTransform(gt);
    8430          21 :     CPLAssert(eErr == CE_None);
    8431          21 :     CPL_IGNORE_RET_VAL(eErr);
    8432             : 
    8433             :     auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
    8434           0 :         std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
    8435          42 :         poReprojectedDS->GetRasterYSize());
    8436             :     auto varY = GDALMDArrayRegularlySpaced::Create(
    8437          63 :         std::string(), poDimY->GetName(), poDimY, gt[3] + gt[5] / 2, gt[5], 0);
    8438          21 :     poDimY->SetIndexingVariable(varY);
    8439             : 
    8440             :     auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
    8441           0 :         std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
    8442          42 :         poReprojectedDS->GetRasterXSize());
    8443             :     auto varX = GDALMDArrayRegularlySpaced::Create(
    8444          63 :         std::string(), poDimX->GetName(), poDimX, gt[0] + gt[1] / 2, gt[1], 0);
    8445          21 :     poDimX->SetIndexingVariable(varX);
    8446             : 
    8447          21 :     apoNewDims.emplace_back(poDimY);
    8448          21 :     apoNewDims.emplace_back(poDimX);
    8449             :     auto newAr(std::shared_ptr<GDALMDArrayResampled>(
    8450          42 :         new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
    8451          21 :     newAr->SetSelf(newAr);
    8452          21 :     if (poTargetSRS)
    8453             :     {
    8454           2 :         newAr->m_poSRS.reset(poTargetSRS->Clone());
    8455             :     }
    8456             :     else
    8457             :     {
    8458          19 :         newAr->m_poSRS = poParent->GetSpatialRef();
    8459             :     }
    8460          21 :     newAr->m_poVarX = varX;
    8461          21 :     newAr->m_poVarY = varY;
    8462          21 :     newAr->m_poReprojectedDS = std::move(poReprojectedDS);
    8463          21 :     newAr->m_poParentDS = std::move(poParentDS);
    8464             : 
    8465             :     // If the input array is y,x,band ordered, the above newAr is
    8466             :     // actually band,y,x ordered as it is more convenient for
    8467             :     // GDALMDArrayResampled::IRead() implementation. But transpose that
    8468             :     // array to the order of the input array
    8469          21 :     if (bYXBandOrder)
    8470           4 :         return newAr->Transpose(std::vector<int>{1, 2, 0});
    8471             : 
    8472          19 :     return newAr;
    8473             : }
    8474             : 
    8475             : /************************************************************************/
    8476             : /*                   GDALMDArrayResampled::IRead()                      */
    8477             : /************************************************************************/
    8478             : 
    8479          29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
    8480             :                                  const size_t *count, const GInt64 *arrayStep,
    8481             :                                  const GPtrDiff_t *bufferStride,
    8482             :                                  const GDALExtendedDataType &bufferDataType,
    8483             :                                  void *pDstBuffer) const
    8484             : {
    8485          29 :     if (bufferDataType.GetClass() != GEDTC_NUMERIC)
    8486           0 :         return false;
    8487             : 
    8488             :     struct Stack
    8489             :     {
    8490             :         size_t nIters = 0;
    8491             :         GByte *dst_ptr = nullptr;
    8492             :         GPtrDiff_t dst_inc_offset = 0;
    8493             :     };
    8494             : 
    8495          29 :     const auto nDims = GetDimensionCount();
    8496          58 :     std::vector<Stack> stack(nDims + 1);  // +1 to avoid -Wnull-dereference
    8497          29 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    8498          92 :     for (size_t i = 0; i < nDims; i++)
    8499             :     {
    8500          63 :         stack[i].dst_inc_offset =
    8501          63 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    8502             :     }
    8503          29 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    8504             : 
    8505          29 :     size_t dimIdx = 0;
    8506          29 :     const size_t iDimY = nDims - 2;
    8507          29 :     const size_t iDimX = nDims - 1;
    8508             :     // Use an array to avoid a false positive warning from CLang Static
    8509             :     // Analyzer about flushCaches being never read
    8510          29 :     bool flushCaches[] = {false};
    8511             :     const bool bYXBandOrder =
    8512          29 :         m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
    8513             : 
    8514          38 : lbl_next_depth:
    8515          38 :     if (dimIdx == iDimY)
    8516             :     {
    8517          33 :         if (flushCaches[0])
    8518             :         {
    8519           5 :             flushCaches[0] = false;
    8520             :             // When changing of 2D slice, flush GDAL 2D buffers
    8521           5 :             m_poParentDS->FlushCache(false);
    8522           5 :             m_poReprojectedDS->FlushCache(false);
    8523             :         }
    8524             : 
    8525          33 :         if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
    8526             :                                     GF_Read, iDimX, iDimY, arrayStartIdx, count,
    8527             :                                     arrayStep, bufferStride, bufferDataType,
    8528          33 :                                     stack[dimIdx].dst_ptr))
    8529             :         {
    8530           0 :             return false;
    8531             :         }
    8532             :     }
    8533             :     else
    8534             :     {
    8535           5 :         stack[dimIdx].nIters = count[dimIdx];
    8536           5 :         if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
    8537           5 :             arrayStartIdx[dimIdx])
    8538             :         {
    8539           1 :             flushCaches[0] = true;
    8540             :         }
    8541           5 :         m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
    8542           5 :             arrayStartIdx[dimIdx];
    8543             :         while (true)
    8544             :         {
    8545           9 :             dimIdx++;
    8546           9 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    8547           9 :             goto lbl_next_depth;
    8548           9 :         lbl_return_to_caller:
    8549           9 :             dimIdx--;
    8550           9 :             if ((--stack[dimIdx].nIters) == 0)
    8551           5 :                 break;
    8552           4 :             flushCaches[0] = true;
    8553           4 :             ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
    8554           4 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    8555             :         }
    8556             :     }
    8557          38 :     if (dimIdx > 0)
    8558           9 :         goto lbl_return_to_caller;
    8559             : 
    8560          29 :     return true;
    8561             : }
    8562             : 
    8563             : /************************************************************************/
    8564             : /*                           GetResampled()                             */
    8565             : /************************************************************************/
    8566             : 
    8567             : /** Return an array that is a resampled / reprojected view of the current array
    8568             :  *
    8569             :  * This is the same as the C function GDALMDArrayGetResampled().
    8570             :  *
    8571             :  * Currently this method can only resample along the last 2 dimensions, unless
    8572             :  * orthorectifying a NASA EMIT dataset.
    8573             :  *
    8574             :  * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
    8575             :  * geometry lookup table (GLT) is used by default for fast orthorectification.
    8576             :  *
    8577             :  * Options available are:
    8578             :  * <ul>
    8579             :  * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
    8580             :  * Can be set to NO to use generic reprojection method.
    8581             :  * </li>
    8582             :  * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
    8583             :  * orthorectification to take into account the value of the
    8584             :  * /sensor_band_parameters/good_wavelengths array to decide if slices of the
    8585             :  * current array along the band dimension are valid.</li>
    8586             :  * </ul>
    8587             :  *
    8588             :  * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
    8589             :  *                   apoNewDims[i] can be NULL to let the method automatically
    8590             :  *                   determine it.
    8591             :  * @param resampleAlg Resampling algorithm
    8592             :  * @param poTargetSRS Target SRS, or nullptr
    8593             :  * @param papszOptions NULL-terminated list of options, or NULL.
    8594             :  *
    8595             :  * @return a new array, that holds a reference to the original one, and thus is
    8596             :  * a view of it (not a copy), or nullptr in case of error.
    8597             :  *
    8598             :  * @since 3.4
    8599             :  */
    8600          38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
    8601             :     const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
    8602             :     GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
    8603             :     CSLConstList papszOptions) const
    8604             : {
    8605          76 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    8606          38 :     if (!self)
    8607             :     {
    8608           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8609             :                  "Driver implementation issue: m_pSelf not set !");
    8610           0 :         return nullptr;
    8611             :     }
    8612          38 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    8613             :     {
    8614           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8615             :                  "GetResampled() only supports numeric data type");
    8616           0 :         return nullptr;
    8617             :     }
    8618             : 
    8619             :     // Special case for NASA EMIT datasets
    8620          76 :     auto apoDims = GetDimensions();
    8621          36 :     if (poTargetSRS == nullptr &&
    8622          59 :         ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
    8623          20 :           apoDims[1]->GetName() == "crosstrack" &&
    8624          10 :           apoDims[2]->GetName() == "bands" &&
    8625          48 :           (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
    8626           1 :            apoNewDims ==
    8627          42 :                std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
    8628          30 :                                                            apoDims[2]})) ||
    8629          51 :          (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
    8630           3 :           apoDims[1]->GetName() == "crosstrack" &&
    8631          77 :           apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
    8632          13 :         CPLTestBool(CSLFetchNameValueDef(papszOptions,
    8633             :                                          "EMIT_ORTHORECTIFICATION", "YES")))
    8634             :     {
    8635           9 :         auto poRootGroup = GetRootGroup();
    8636           9 :         if (poRootGroup)
    8637             :         {
    8638          18 :             auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
    8639          18 :             auto poLocationGroup = poRootGroup->OpenGroup("location");
    8640           9 :             if (poAttrGeotransform &&
    8641           9 :                 poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
    8642           9 :                 poAttrGeotransform->GetDimensionCount() == 1 &&
    8643          27 :                 poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
    8644           9 :                 poLocationGroup)
    8645             :             {
    8646          18 :                 auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
    8647          18 :                 auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
    8648          27 :                 if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
    8649          18 :                     poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
    8650          18 :                     poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
    8651          27 :                     poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
    8652          27 :                     poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
    8653           9 :                     poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
    8654             :                 {
    8655             :                     return CreateGLTOrthorectified(
    8656             :                         self, poRootGroup, poGLT_X, poGLT_Y,
    8657             :                         /* nGLTIndexOffset = */ -1,
    8658          18 :                         poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
    8659             :                 }
    8660             :             }
    8661             :         }
    8662             :     }
    8663             : 
    8664          29 :     if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
    8665             :                                          "EMIT_ORTHORECTIFICATION", "NO")))
    8666             :     {
    8667           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8668             :                  "EMIT_ORTHORECTIFICATION required, but dataset and/or "
    8669             :                  "parameters are not compatible with it");
    8670           0 :         return nullptr;
    8671             :     }
    8672             : 
    8673             :     return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
    8674          29 :                                         poTargetSRS, papszOptions);
    8675             : }
    8676             : 
    8677             : /************************************************************************/
    8678             : /*                         GDALDatasetFromArray()                       */
    8679             : /************************************************************************/
    8680             : 
    8681             : class GDALDatasetFromArray;
    8682             : 
    8683             : namespace
    8684             : {
    8685             : struct MetadataItem
    8686             : {
    8687             :     std::shared_ptr<GDALAbstractMDArray> poArray{};
    8688             :     std::string osName{};
    8689             :     std::string osDefinition{};
    8690             :     bool bDefinitionUsesPctForG = false;
    8691             : };
    8692             : 
    8693             : struct BandImageryMetadata
    8694             : {
    8695             :     std::shared_ptr<GDALAbstractMDArray> poCentralWavelengthArray{};
    8696             :     double dfCentralWavelengthToMicrometer = 1.0;
    8697             :     std::shared_ptr<GDALAbstractMDArray> poFWHMArray{};
    8698             :     double dfFWHMToMicrometer = 1.0;
    8699             : };
    8700             : 
    8701             : }  // namespace
    8702             : 
    8703             : class GDALRasterBandFromArray final : public GDALPamRasterBand
    8704             : {
    8705             :     std::vector<GUInt64> m_anOffset{};
    8706             :     std::vector<size_t> m_anCount{};
    8707             :     std::vector<GPtrDiff_t> m_anStride{};
    8708             : 
    8709             :   protected:
    8710             :     CPLErr IReadBlock(int, int, void *) override;
    8711             :     CPLErr IWriteBlock(int, int, void *) override;
    8712             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
    8713             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
    8714             :                      GDALDataType eBufType, GSpacing nPixelSpaceBuf,
    8715             :                      GSpacing nLineSpaceBuf,
    8716             :                      GDALRasterIOExtraArg *psExtraArg) override;
    8717             : 
    8718             :   public:
    8719             :     explicit GDALRasterBandFromArray(
    8720             :         GDALDatasetFromArray *poDSIn,
    8721             :         const std::vector<GUInt64> &anOtherDimCoord,
    8722             :         const std::vector<std::vector<MetadataItem>>
    8723             :             &aoBandParameterMetadataItems,
    8724             :         const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
    8725             :         double dfDelay, time_t nStartTime, bool &bHasWarned);
    8726             : 
    8727             :     double GetNoDataValue(int *pbHasNoData) override;
    8728             :     int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
    8729             :     uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
    8730             :     double GetOffset(int *pbHasOffset) override;
    8731             :     double GetScale(int *pbHasScale) override;
    8732             :     const char *GetUnitType() override;
    8733             :     GDALColorInterp GetColorInterpretation() override;
    8734             : };
    8735             : 
    8736             : class GDALDatasetFromArray final : public GDALPamDataset
    8737             : {
    8738             :     friend class GDALRasterBandFromArray;
    8739             : 
    8740             :     std::shared_ptr<GDALMDArray> m_poArray;
    8741             :     size_t m_iXDim;
    8742             :     size_t m_iYDim;
    8743             :     GDALGeoTransform m_gt{};
    8744             :     bool m_bHasGT = false;
    8745             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
    8746             :     GDALMultiDomainMetadata m_oMDD{};
    8747             :     std::string m_osOvrFilename{};
    8748             : 
    8749             :   public:
    8750         242 :     GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
    8751             :                          size_t iXDim, size_t iYDim)
    8752         242 :         : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
    8753             :     {
    8754             :         // Initialize an overview filename from the filename of the array
    8755             :         // and its name.
    8756         242 :         const std::string &osFilename = m_poArray->GetFilename();
    8757         242 :         if (!osFilename.empty())
    8758             :         {
    8759         205 :             m_osOvrFilename = osFilename;
    8760         205 :             m_osOvrFilename += '.';
    8761        8066 :             for (char ch : m_poArray->GetName())
    8762             :             {
    8763        7861 :                 if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
    8764        6956 :                     (ch >= 'a' && ch <= 'z') || ch == '_')
    8765             :                 {
    8766        6290 :                     m_osOvrFilename += ch;
    8767             :                 }
    8768             :                 else
    8769             :                 {
    8770        1571 :                     m_osOvrFilename += '_';
    8771             :                 }
    8772             :             }
    8773         205 :             m_osOvrFilename += ".ovr";
    8774         205 :             oOvManager.Initialize(this);
    8775             :         }
    8776         242 :     }
    8777             : 
    8778             :     static GDALDatasetFromArray *
    8779             :     Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
    8780             :            size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
    8781             :            CSLConstList papszOptions);
    8782             : 
    8783             :     ~GDALDatasetFromArray() override;
    8784             : 
    8785         391 :     CPLErr Close() override
    8786             :     {
    8787         391 :         CPLErr eErr = CE_None;
    8788         391 :         if (nOpenFlags != OPEN_FLAGS_CLOSED)
    8789             :         {
    8790         391 :             if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
    8791             :                 CE_None)
    8792           0 :                 eErr = CE_Failure;
    8793         391 :             m_poArray.reset();
    8794             :         }
    8795         391 :         return eErr;
    8796             :     }
    8797             : 
    8798          73 :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override
    8799             :     {
    8800          73 :         gt = m_gt;
    8801          73 :         return m_bHasGT ? CE_None : CE_Failure;
    8802             :     }
    8803             : 
    8804          70 :     const OGRSpatialReference *GetSpatialRef() const override
    8805             :     {
    8806          70 :         if (m_poArray->GetDimensionCount() < 2)
    8807           3 :             return nullptr;
    8808          67 :         m_poSRS = m_poArray->GetSpatialRef();
    8809          67 :         if (m_poSRS)
    8810             :         {
    8811          28 :             m_poSRS.reset(m_poSRS->Clone());
    8812          56 :             auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
    8813          84 :             for (auto &m : axisMapping)
    8814             :             {
    8815          56 :                 if (m == static_cast<int>(m_iXDim) + 1)
    8816          28 :                     m = 1;
    8817          28 :                 else if (m == static_cast<int>(m_iYDim) + 1)
    8818          28 :                     m = 2;
    8819             :             }
    8820          28 :             m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
    8821             :         }
    8822          67 :         return m_poSRS.get();
    8823             :     }
    8824             : 
    8825           6 :     CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
    8826             :     {
    8827           6 :         return m_oMDD.SetMetadata(papszMetadata, pszDomain);
    8828             :     }
    8829             : 
    8830         179 :     char **GetMetadata(const char *pszDomain) override
    8831             :     {
    8832         179 :         return m_oMDD.GetMetadata(pszDomain);
    8833             :     }
    8834             : 
    8835         237 :     const char *GetMetadataItem(const char *pszName,
    8836             :                                 const char *pszDomain) override
    8837             :     {
    8838         429 :         if (!m_osOvrFilename.empty() && pszName &&
    8839         444 :             EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
    8840          15 :             EQUAL(pszDomain, "OVERVIEWS"))
    8841             :         {
    8842          15 :             return m_osOvrFilename.c_str();
    8843             :         }
    8844         222 :         return m_oMDD.GetMetadataItem(pszName, pszDomain);
    8845             :     }
    8846             : };
    8847             : 
    8848         484 : GDALDatasetFromArray::~GDALDatasetFromArray()
    8849             : {
    8850         242 :     GDALDatasetFromArray::Close();
    8851         484 : }
    8852             : 
    8853             : /************************************************************************/
    8854             : /*                      GDALRasterBandFromArray()                       */
    8855             : /************************************************************************/
    8856             : 
    8857         318 : GDALRasterBandFromArray::GDALRasterBandFromArray(
    8858             :     GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
    8859             :     const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
    8860             :     const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
    8861         318 :     double dfDelay, time_t nStartTime, bool &bHasWarned)
    8862             : {
    8863         318 :     const auto &poArray(poDSIn->m_poArray);
    8864         318 :     const auto &dims(poArray->GetDimensions());
    8865         318 :     const auto nDimCount(dims.size());
    8866         636 :     const auto blockSize(poArray->GetBlockSize());
    8867         302 :     nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
    8868         620 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    8869         150 :                                                   blockSize[poDSIn->m_iYDim]))
    8870             :                       : 1;
    8871         318 :     nBlockXSize = blockSize[poDSIn->m_iXDim]
    8872         165 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    8873         165 :                                                   blockSize[poDSIn->m_iXDim]))
    8874         318 :                       : poDSIn->GetRasterXSize();
    8875         318 :     eDataType = poArray->GetDataType().GetNumericDataType();
    8876         318 :     eAccess = poDSIn->eAccess;
    8877         318 :     m_anOffset.resize(nDimCount);
    8878         318 :     m_anCount.resize(nDimCount, 1);
    8879         318 :     m_anStride.resize(nDimCount);
    8880        1069 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    8881             :     {
    8882         751 :         if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
    8883             :         {
    8884         262 :             std::string dimName(dims[i]->GetName());
    8885         131 :             GUInt64 nIndex = anOtherDimCoord[j];
    8886             :             // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
    8887             :             // subsetted dimensions as generated by GetView()
    8888         131 :             if (STARTS_WITH(dimName.c_str(), "subset_"))
    8889             :             {
    8890             :                 CPLStringList aosTokens(
    8891          12 :                     CSLTokenizeString2(dimName.c_str(), "_", 0));
    8892           6 :                 if (aosTokens.size() == 5)
    8893             :                 {
    8894           6 :                     dimName = aosTokens[1];
    8895          18 :                     const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
    8896           6 :                         aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
    8897           6 :                     const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
    8898           6 :                     nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
    8899           0 :                                           : nStartDim - (nIndex * -nIncrDim);
    8900             :                 }
    8901             :             }
    8902         131 :             if (nDimCount != 3 || dimName != "Band")
    8903             :             {
    8904          70 :                 SetMetadataItem(
    8905             :                     CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
    8906             :                     CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
    8907             :             }
    8908             : 
    8909         131 :             auto indexingVar = dims[i]->GetIndexingVariable();
    8910             : 
    8911             :             // If the indexing variable is also listed in band parameter arrays,
    8912             :             // then don't use our default formatting
    8913         131 :             if (indexingVar)
    8914             :             {
    8915          47 :                 for (const auto &oItem : aoBandParameterMetadataItems[j])
    8916             :                 {
    8917          14 :                     if (oItem.poArray->GetFullName() ==
    8918          14 :                         indexingVar->GetFullName())
    8919             :                     {
    8920          12 :                         indexingVar.reset();
    8921          12 :                         break;
    8922             :                     }
    8923             :                 }
    8924             :             }
    8925             : 
    8926         164 :             if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
    8927          33 :                 indexingVar->GetDimensions()[0]->GetSize() ==
    8928          33 :                     dims[i]->GetSize())
    8929             :             {
    8930          33 :                 if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
    8931             :                 {
    8932           0 :                     if (!bHasWarned)
    8933             :                     {
    8934           0 :                         CPLError(
    8935             :                             CE_Warning, CPLE_AppDefined,
    8936             :                             "Maximum delay to load band metadata from "
    8937             :                             "dimension indexing variables has expired. "
    8938             :                             "Increase the value of the "
    8939             :                             "LOAD_EXTRA_DIM_METADATA_DELAY "
    8940             :                             "option of GDALMDArray::AsClassicDataset() "
    8941             :                             "(also accessible as the "
    8942             :                             "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
    8943             :                             "configuration option), "
    8944             :                             "or set it to 'unlimited' for unlimited delay. ");
    8945           0 :                         bHasWarned = true;
    8946             :                     }
    8947             :                 }
    8948             :                 else
    8949             :                 {
    8950          33 :                     size_t nCount = 1;
    8951          33 :                     const auto &dt(indexingVar->GetDataType());
    8952          66 :                     std::vector<GByte> abyTmp(dt.GetSize());
    8953          66 :                     if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
    8954          33 :                                           nullptr, nullptr, dt, &abyTmp[0]))
    8955             :                     {
    8956          33 :                         char *pszTmp = nullptr;
    8957          33 :                         GDALExtendedDataType::CopyValue(
    8958          33 :                             &abyTmp[0], dt, &pszTmp,
    8959          66 :                             GDALExtendedDataType::CreateString());
    8960          33 :                         dt.FreeDynamicMemory(abyTmp.data());
    8961          33 :                         if (pszTmp)
    8962             :                         {
    8963          33 :                             SetMetadataItem(
    8964             :                                 CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
    8965             :                                 pszTmp);
    8966          33 :                             CPLFree(pszTmp);
    8967             :                         }
    8968             : 
    8969          33 :                         const auto &unit(indexingVar->GetUnit());
    8970          33 :                         if (!unit.empty())
    8971             :                         {
    8972          12 :                             SetMetadataItem(
    8973             :                                 CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
    8974             :                                 unit.c_str());
    8975             :                         }
    8976             :                     }
    8977             :                 }
    8978             :             }
    8979             : 
    8980         149 :             for (const auto &oItem : aoBandParameterMetadataItems[j])
    8981             :             {
    8982          36 :                 CPLString osVal;
    8983             : 
    8984          18 :                 size_t nCount = 1;
    8985          18 :                 const auto &dt(oItem.poArray->GetDataType());
    8986          18 :                 if (oItem.bDefinitionUsesPctForG)
    8987             :                 {
    8988             :                     // There is one and only one %[x][.y]f|g in osDefinition
    8989          16 :                     std::vector<GByte> abyTmp(dt.GetSize());
    8990          16 :                     if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
    8991           8 :                                             nullptr, nullptr, dt, &abyTmp[0]))
    8992             :                     {
    8993           8 :                         double dfVal = 0;
    8994           8 :                         GDALExtendedDataType::CopyValue(
    8995           8 :                             &abyTmp[0], dt, &dfVal,
    8996          16 :                             GDALExtendedDataType::Create(GDT_Float64));
    8997           8 :                         osVal.Printf(oItem.osDefinition.c_str(), dfVal);
    8998           8 :                         dt.FreeDynamicMemory(abyTmp.data());
    8999             :                     }
    9000             :                 }
    9001             :                 else
    9002             :                 {
    9003             :                     // There should be zero or one %s in osDefinition
    9004          10 :                     char *pszValue = nullptr;
    9005          10 :                     if (dt.GetClass() == GEDTC_STRING)
    9006             :                     {
    9007           4 :                         CPL_IGNORE_RET_VAL(oItem.poArray->Read(
    9008           2 :                             &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
    9009           2 :                             dt, &pszValue));
    9010             :                     }
    9011             :                     else
    9012             :                     {
    9013          16 :                         std::vector<GByte> abyTmp(dt.GetSize());
    9014          16 :                         if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
    9015             :                                                 nullptr, nullptr, dt,
    9016           8 :                                                 &abyTmp[0]))
    9017             :                         {
    9018           8 :                             GDALExtendedDataType::CopyValue(
    9019           8 :                                 &abyTmp[0], dt, &pszValue,
    9020          16 :                                 GDALExtendedDataType::CreateString());
    9021             :                         }
    9022             :                     }
    9023             : 
    9024          10 :                     if (pszValue)
    9025             :                     {
    9026          10 :                         osVal.Printf(oItem.osDefinition.c_str(), pszValue);
    9027          10 :                         CPLFree(pszValue);
    9028             :                     }
    9029             :                 }
    9030          18 :                 if (!osVal.empty())
    9031          18 :                     SetMetadataItem(oItem.osName.c_str(), osVal);
    9032             :             }
    9033             : 
    9034         131 :             if (aoBandImageryMetadata[j].poCentralWavelengthArray)
    9035             :             {
    9036             :                 auto &poCentralWavelengthArray =
    9037           4 :                     aoBandImageryMetadata[j].poCentralWavelengthArray;
    9038           4 :                 size_t nCount = 1;
    9039           4 :                 const auto &dt(poCentralWavelengthArray->GetDataType());
    9040           8 :                 std::vector<GByte> abyTmp(dt.GetSize());
    9041           8 :                 if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
    9042             :                                                    &nCount, nullptr, nullptr,
    9043           4 :                                                    dt, &abyTmp[0]))
    9044             :                 {
    9045           4 :                     double dfVal = 0;
    9046           4 :                     GDALExtendedDataType::CopyValue(
    9047           4 :                         &abyTmp[0], dt, &dfVal,
    9048           8 :                         GDALExtendedDataType::Create(GDT_Float64));
    9049           4 :                     dt.FreeDynamicMemory(abyTmp.data());
    9050           4 :                     SetMetadataItem(
    9051             :                         "CENTRAL_WAVELENGTH_UM",
    9052             :                         CPLSPrintf(
    9053           4 :                             "%g", dfVal * aoBandImageryMetadata[j]
    9054           4 :                                               .dfCentralWavelengthToMicrometer),
    9055             :                         "IMAGERY");
    9056             :                 }
    9057             :             }
    9058             : 
    9059         131 :             if (aoBandImageryMetadata[j].poFWHMArray)
    9060             :             {
    9061           2 :                 auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
    9062           2 :                 size_t nCount = 1;
    9063           2 :                 const auto &dt(poFWHMArray->GetDataType());
    9064           4 :                 std::vector<GByte> abyTmp(dt.GetSize());
    9065           4 :                 if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
    9066           2 :                                       nullptr, dt, &abyTmp[0]))
    9067             :                 {
    9068           2 :                     double dfVal = 0;
    9069           2 :                     GDALExtendedDataType::CopyValue(
    9070           2 :                         &abyTmp[0], dt, &dfVal,
    9071           4 :                         GDALExtendedDataType::Create(GDT_Float64));
    9072           2 :                     dt.FreeDynamicMemory(abyTmp.data());
    9073           2 :                     SetMetadataItem(
    9074             :                         "FWHM_UM",
    9075           2 :                         CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
    9076           2 :                                                      .dfFWHMToMicrometer),
    9077             :                         "IMAGERY");
    9078             :                 }
    9079             :             }
    9080             : 
    9081         131 :             m_anOffset[i] = anOtherDimCoord[j];
    9082         131 :             j++;
    9083             :         }
    9084             :     }
    9085         318 : }
    9086             : 
    9087             : /************************************************************************/
    9088             : /*                           GetNoDataValue()                           */
    9089             : /************************************************************************/
    9090             : 
    9091         114 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
    9092             : {
    9093         114 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9094         114 :     const auto &poArray(l_poDS->m_poArray);
    9095         114 :     bool bHasNodata = false;
    9096         114 :     const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
    9097         114 :     if (pbHasNoData)
    9098         102 :         *pbHasNoData = bHasNodata;
    9099         114 :     return res;
    9100             : }
    9101             : 
    9102             : /************************************************************************/
    9103             : /*                       GetNoDataValueAsInt64()                        */
    9104             : /************************************************************************/
    9105             : 
    9106           1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
    9107             : {
    9108           1 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9109           1 :     const auto &poArray(l_poDS->m_poArray);
    9110           1 :     bool bHasNodata = false;
    9111           1 :     const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
    9112           1 :     if (pbHasNoData)
    9113           1 :         *pbHasNoData = bHasNodata;
    9114           1 :     return nodata;
    9115             : }
    9116             : 
    9117             : /************************************************************************/
    9118             : /*                      GetNoDataValueAsUInt64()                        */
    9119             : /************************************************************************/
    9120             : 
    9121           1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
    9122             : {
    9123           1 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9124           1 :     const auto &poArray(l_poDS->m_poArray);
    9125           1 :     bool bHasNodata = false;
    9126           1 :     const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
    9127           1 :     if (pbHasNoData)
    9128           1 :         *pbHasNoData = bHasNodata;
    9129           1 :     return nodata;
    9130             : }
    9131             : 
    9132             : /************************************************************************/
    9133             : /*                             GetOffset()                              */
    9134             : /************************************************************************/
    9135             : 
    9136          40 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
    9137             : {
    9138          40 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9139          40 :     const auto &poArray(l_poDS->m_poArray);
    9140          40 :     bool bHasValue = false;
    9141          40 :     double dfRes = poArray->GetOffset(&bHasValue);
    9142          40 :     if (pbHasOffset)
    9143          21 :         *pbHasOffset = bHasValue;
    9144          40 :     return dfRes;
    9145             : }
    9146             : 
    9147             : /************************************************************************/
    9148             : /*                           GetUnitType()                              */
    9149             : /************************************************************************/
    9150             : 
    9151          46 : const char *GDALRasterBandFromArray::GetUnitType()
    9152             : {
    9153          46 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9154          46 :     const auto &poArray(l_poDS->m_poArray);
    9155          46 :     return poArray->GetUnit().c_str();
    9156             : }
    9157             : 
    9158             : /************************************************************************/
    9159             : /*                             GetScale()                              */
    9160             : /************************************************************************/
    9161             : 
    9162          38 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
    9163             : {
    9164          38 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9165          38 :     const auto &poArray(l_poDS->m_poArray);
    9166          38 :     bool bHasValue = false;
    9167          38 :     double dfRes = poArray->GetScale(&bHasValue);
    9168          38 :     if (pbHasScale)
    9169          19 :         *pbHasScale = bHasValue;
    9170          38 :     return dfRes;
    9171             : }
    9172             : 
    9173             : /************************************************************************/
    9174             : /*                            IReadBlock()                              */
    9175             : /************************************************************************/
    9176             : 
    9177          68 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
    9178             :                                            void *pImage)
    9179             : {
    9180          68 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    9181          68 :     const int nXOff = nBlockXOff * nBlockXSize;
    9182          68 :     const int nYOff = nBlockYOff * nBlockYSize;
    9183          68 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    9184          68 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    9185             :     GDALRasterIOExtraArg sExtraArg;
    9186          68 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    9187         136 :     return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    9188             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    9189         136 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    9190             : }
    9191             : 
    9192             : /************************************************************************/
    9193             : /*                            IWriteBlock()                             */
    9194             : /************************************************************************/
    9195             : 
    9196           1 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
    9197             :                                             void *pImage)
    9198             : {
    9199           1 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    9200           1 :     const int nXOff = nBlockXOff * nBlockXSize;
    9201           1 :     const int nYOff = nBlockYOff * nBlockYSize;
    9202           1 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    9203           1 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    9204             :     GDALRasterIOExtraArg sExtraArg;
    9205           1 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    9206           2 :     return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    9207             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    9208           2 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    9209             : }
    9210             : 
    9211             : /************************************************************************/
    9212             : /*                            IRasterIO()                               */
    9213             : /************************************************************************/
    9214             : 
    9215         400 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
    9216             :                                           int nYOff, int nXSize, int nYSize,
    9217             :                                           void *pData, int nBufXSize,
    9218             :                                           int nBufYSize, GDALDataType eBufType,
    9219             :                                           GSpacing nPixelSpaceBuf,
    9220             :                                           GSpacing nLineSpaceBuf,
    9221             :                                           GDALRasterIOExtraArg *psExtraArg)
    9222             : {
    9223         400 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9224         400 :     const auto &poArray(l_poDS->m_poArray);
    9225         400 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
    9226         400 :     if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
    9227         400 :         (nPixelSpaceBuf % nBufferDTSize) == 0 &&
    9228         400 :         (nLineSpaceBuf % nBufferDTSize) == 0)
    9229             :     {
    9230         400 :         m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
    9231         400 :         m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
    9232         800 :         m_anStride[l_poDS->m_iXDim] =
    9233         400 :             static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
    9234         400 :         if (poArray->GetDimensionCount() >= 2)
    9235             :         {
    9236         387 :             m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
    9237         387 :             m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
    9238         387 :             m_anStride[l_poDS->m_iYDim] =
    9239         387 :                 static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
    9240             :         }
    9241         400 :         if (eRWFlag == GF_Read)
    9242             :         {
    9243         788 :             return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
    9244         394 :                                  m_anStride.data(),
    9245         788 :                                  GDALExtendedDataType::Create(eBufType), pData)
    9246         394 :                        ? CE_None
    9247         394 :                        : CE_Failure;
    9248             :         }
    9249             :         else
    9250             :         {
    9251          12 :             return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
    9252           6 :                                   m_anStride.data(),
    9253          12 :                                   GDALExtendedDataType::Create(eBufType), pData)
    9254           6 :                        ? CE_None
    9255           6 :                        : CE_Failure;
    9256             :         }
    9257             :     }
    9258           0 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    9259             :                                      pData, nBufXSize, nBufYSize, eBufType,
    9260           0 :                                      nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
    9261             : }
    9262             : 
    9263             : /************************************************************************/
    9264             : /*                      GetColorInterpretation()                        */
    9265             : /************************************************************************/
    9266             : 
    9267          66 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
    9268             : {
    9269          66 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9270          66 :     const auto &poArray(l_poDS->m_poArray);
    9271         198 :     auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
    9272          66 :     if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
    9273             :     {
    9274           6 :         bool bOK = false;
    9275           6 :         GUInt64 nStartIndex = 0;
    9276           6 :         if (poArray->GetDimensionCount() == 2 &&
    9277           0 :             poAttr->GetDimensionCount() == 0)
    9278             :         {
    9279           0 :             bOK = true;
    9280             :         }
    9281           6 :         else if (poArray->GetDimensionCount() == 3)
    9282             :         {
    9283           6 :             uint64_t nExtraDimSamples = 1;
    9284           6 :             const auto &apoDims = poArray->GetDimensions();
    9285          24 :             for (size_t i = 0; i < apoDims.size(); ++i)
    9286             :             {
    9287          18 :                 if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
    9288           6 :                     nExtraDimSamples *= apoDims[i]->GetSize();
    9289             :             }
    9290           6 :             if (poAttr->GetDimensionsSize() ==
    9291          12 :                 std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
    9292             :             {
    9293           6 :                 bOK = true;
    9294             :             }
    9295           6 :             nStartIndex = nBand - 1;
    9296             :         }
    9297           6 :         if (bOK)
    9298             :         {
    9299           6 :             const auto oStringDT = GDALExtendedDataType::CreateString();
    9300           6 :             const size_t nCount = 1;
    9301           6 :             const GInt64 arrayStep = 1;
    9302           6 :             const GPtrDiff_t bufferStride = 1;
    9303           6 :             char *pszValue = nullptr;
    9304           6 :             poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
    9305           6 :                          oStringDT, &pszValue);
    9306           6 :             if (pszValue)
    9307             :             {
    9308             :                 const auto eColorInterp =
    9309           6 :                     GDALGetColorInterpretationByName(pszValue);
    9310           6 :                 CPLFree(pszValue);
    9311           6 :                 return eColorInterp;
    9312             :             }
    9313             :         }
    9314             :     }
    9315          60 :     return GCI_Undefined;
    9316             : }
    9317             : 
    9318             : /************************************************************************/
    9319             : /*                    GDALDatasetFromArray::Create()                    */
    9320             : /************************************************************************/
    9321             : 
    9322         294 : GDALDatasetFromArray *GDALDatasetFromArray::Create(
    9323             :     const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
    9324             :     const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
    9325             : 
    9326             : {
    9327         294 :     const auto nDimCount(array->GetDimensionCount());
    9328         294 :     if (nDimCount == 0)
    9329             :     {
    9330           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    9331             :                  "Unsupported number of dimensions");
    9332           1 :         return nullptr;
    9333             :     }
    9334         585 :     if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
    9335         292 :         array->GetDataType().GetNumericDataType() == GDT_Unknown)
    9336             :     {
    9337           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    9338             :                  "Only arrays with numeric data types "
    9339             :                  "can be exposed as classic GDALDataset");
    9340           1 :         return nullptr;
    9341             :     }
    9342         292 :     if (iXDim >= nDimCount || iYDim >= nDimCount ||
    9343         269 :         (nDimCount >= 2 && iXDim == iYDim))
    9344             :     {
    9345           8 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
    9346           8 :         return nullptr;
    9347             :     }
    9348         284 :     GUInt64 nTotalBands = 1;
    9349         284 :     const auto &dims(array->GetDimensions());
    9350         927 :     for (size_t i = 0; i < nDimCount; ++i)
    9351             :     {
    9352         644 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9353             :         {
    9354          94 :             if (dims[i]->GetSize() > 65536 / nTotalBands)
    9355             :             {
    9356           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9357             :                          "Too many bands. Operate on a sliced view");
    9358           1 :                 return nullptr;
    9359             :             }
    9360          93 :             nTotalBands *= dims[i]->GetSize();
    9361             :         }
    9362             :     }
    9363             : 
    9364         566 :     std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
    9365         566 :     std::vector<size_t> oMapArrayExtraDimIdxToOriginalIdx;
    9366         926 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    9367             :     {
    9368         643 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9369             :         {
    9370          93 :             oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
    9371          93 :             oMapArrayExtraDimIdxToOriginalIdx.push_back(i);
    9372          93 :             ++j;
    9373             :         }
    9374             :     }
    9375             : 
    9376         283 :     const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
    9377             : 
    9378             :     const char *pszBandMetadata =
    9379         283 :         CSLFetchNameValue(papszOptions, "BAND_METADATA");
    9380             :     std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
    9381         566 :         nNewDimCount);
    9382         283 :     if (pszBandMetadata)
    9383             :     {
    9384          32 :         if (!poRootGroup)
    9385             :         {
    9386           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9387             :                      "Root group should be provided when BAND_METADATA is set");
    9388          24 :             return nullptr;
    9389             :         }
    9390          31 :         CPLJSONDocument oDoc;
    9391          31 :         if (!oDoc.LoadMemory(pszBandMetadata))
    9392             :         {
    9393           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9394             :                      "Invalid JSON content for BAND_METADATA");
    9395           1 :             return nullptr;
    9396             :         }
    9397          30 :         auto oRoot = oDoc.GetRoot();
    9398          30 :         if (oRoot.GetType() != CPLJSONObject::Type::Array)
    9399             :         {
    9400           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9401             :                      "Value of BAND_METADATA should be an array");
    9402           1 :             return nullptr;
    9403             :         }
    9404             : 
    9405          29 :         auto oArray = oRoot.ToArray();
    9406          38 :         for (int j = 0; j < oArray.Size(); ++j)
    9407             :         {
    9408          30 :             const auto oJsonItem = oArray[j];
    9409          30 :             MetadataItem oItem;
    9410          30 :             size_t iExtraDimIdx = 0;
    9411             : 
    9412          60 :             const auto osBandArrayFullname = oJsonItem.GetString("array");
    9413          60 :             const auto osBandAttributeName = oJsonItem.GetString("attribute");
    9414           0 :             std::shared_ptr<GDALMDArray> poArray;
    9415           0 :             std::shared_ptr<GDALAttribute> poAttribute;
    9416          30 :             if (osBandArrayFullname.empty() && osBandAttributeName.empty())
    9417             :             {
    9418           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9419             :                          "BAND_METADATA[%d][\"array\"] or "
    9420             :                          "BAND_METADATA[%d][\"attribute\"] is missing",
    9421             :                          j, j);
    9422           1 :                 return nullptr;
    9423             :             }
    9424          48 :             else if (!osBandArrayFullname.empty() &&
    9425          19 :                      !osBandAttributeName.empty())
    9426             :             {
    9427           1 :                 CPLError(
    9428             :                     CE_Failure, CPLE_AppDefined,
    9429             :                     "BAND_METADATA[%d][\"array\"] and "
    9430             :                     "BAND_METADATA[%d][\"attribute\"] are mutually exclusive",
    9431             :                     j, j);
    9432           1 :                 return nullptr;
    9433             :             }
    9434          28 :             else if (!osBandArrayFullname.empty())
    9435             :             {
    9436             :                 poArray =
    9437          18 :                     poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
    9438          18 :                 if (!poArray)
    9439             :                 {
    9440           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9441             :                              "Array %s cannot be found",
    9442             :                              osBandArrayFullname.c_str());
    9443           3 :                     return nullptr;
    9444             :                 }
    9445          17 :                 if (poArray->GetDimensionCount() != 1)
    9446             :                 {
    9447           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9448             :                              "Array %s is not a 1D array",
    9449             :                              osBandArrayFullname.c_str());
    9450           1 :                     return nullptr;
    9451             :                 }
    9452             :                 const auto &osAuxArrayDimName =
    9453          16 :                     poArray->GetDimensions()[0]->GetName();
    9454             :                 auto oIter =
    9455          16 :                     oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
    9456          16 :                 if (oIter == oMapArrayDimNameToExtraDimIdx.end())
    9457             :                 {
    9458           1 :                     CPLError(
    9459             :                         CE_Failure, CPLE_AppDefined,
    9460             :                         "Dimension %s of array %s is not a non-X/Y dimension "
    9461             :                         "of array %s",
    9462             :                         osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
    9463           1 :                         array->GetName().c_str());
    9464           1 :                     return nullptr;
    9465             :                 }
    9466          15 :                 iExtraDimIdx = oIter->second;
    9467          15 :                 CPLAssert(iExtraDimIdx < nNewDimCount);
    9468             :             }
    9469             :             else
    9470             :             {
    9471          10 :                 CPLAssert(!osBandAttributeName.empty());
    9472          10 :                 poAttribute = !osBandAttributeName.empty() &&
    9473          10 :                                       osBandAttributeName[0] == '/'
    9474          24 :                                   ? poRootGroup->OpenAttributeFromFullname(
    9475             :                                         osBandAttributeName)
    9476          10 :                                   : array->GetAttribute(osBandAttributeName);
    9477          10 :                 if (!poAttribute)
    9478             :                 {
    9479           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9480             :                              "Attribute %s cannot be found",
    9481             :                              osBandAttributeName.c_str());
    9482           8 :                     return nullptr;
    9483             :                 }
    9484           8 :                 const auto aoAttrDims = poAttribute->GetDimensionsSize();
    9485           8 :                 if (aoAttrDims.size() != 1)
    9486             :                 {
    9487           4 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9488             :                              "Attribute %s is not a 1D array",
    9489             :                              osBandAttributeName.c_str());
    9490           4 :                     return nullptr;
    9491             :                 }
    9492           4 :                 bool found = false;
    9493           8 :                 for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
    9494             :                 {
    9495           5 :                     if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
    9496           5 :                             ->GetSize() == aoAttrDims[0])
    9497             :                     {
    9498           4 :                         if (found)
    9499             :                         {
    9500           2 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9501             :                                      "Several dimensions of %s have the same "
    9502             :                                      "size as attribute %s. Cannot infer which "
    9503             :                                      "one to bind to!",
    9504           1 :                                      array->GetName().c_str(),
    9505             :                                      osBandAttributeName.c_str());
    9506           1 :                             return nullptr;
    9507             :                         }
    9508           3 :                         found = true;
    9509           3 :                         iExtraDimIdx = iter.second;
    9510             :                     }
    9511             :                 }
    9512           3 :                 if (!found)
    9513             :                 {
    9514           2 :                     CPLError(
    9515             :                         CE_Failure, CPLE_AppDefined,
    9516             :                         "No dimension of %s has the same size as attribute %s",
    9517           1 :                         array->GetName().c_str(), osBandAttributeName.c_str());
    9518           1 :                     return nullptr;
    9519             :                 }
    9520             :             }
    9521             : 
    9522          17 :             oItem.osName = oJsonItem.GetString("item_name");
    9523          17 :             if (oItem.osName.empty())
    9524             :             {
    9525           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9526             :                          "BAND_METADATA[%d][\"item_name\"] is missing", j);
    9527           1 :                 return nullptr;
    9528             :             }
    9529             : 
    9530          32 :             const auto osDefinition = oJsonItem.GetString("item_value", "%s");
    9531             : 
    9532             :             // Check correctness of definition
    9533          16 :             bool bFirstNumericFormatter = true;
    9534          16 :             std::string osModDefinition;
    9535          16 :             bool bDefinitionUsesPctForG = false;
    9536          79 :             for (size_t k = 0; k < osDefinition.size(); ++k)
    9537             :             {
    9538          70 :                 if (osDefinition[k] == '%')
    9539             :                 {
    9540          15 :                     osModDefinition += osDefinition[k];
    9541          15 :                     if (k + 1 == osDefinition.size())
    9542             :                     {
    9543           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9544             :                                  "Value of "
    9545             :                                  "BAND_METADATA[%d][\"item_value\"] = "
    9546             :                                  "%s is invalid at offset %d",
    9547             :                                  j, osDefinition.c_str(), int(k));
    9548           1 :                         return nullptr;
    9549             :                     }
    9550          14 :                     ++k;
    9551          14 :                     if (osDefinition[k] == '%')
    9552             :                     {
    9553           1 :                         osModDefinition += osDefinition[k];
    9554           1 :                         continue;
    9555             :                     }
    9556          13 :                     if (!bFirstNumericFormatter)
    9557             :                     {
    9558           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9559             :                                  "Value of "
    9560             :                                  "BAND_METADATA[%d][\"item_value\"] = %s is "
    9561             :                                  "invalid at offset %d: %%[x][.y]f|g or %%s "
    9562             :                                  "formatters should be specified at most once",
    9563             :                                  j, osDefinition.c_str(), int(k));
    9564           1 :                         return nullptr;
    9565             :                     }
    9566          12 :                     bFirstNumericFormatter = false;
    9567          19 :                     for (; k < osDefinition.size(); ++k)
    9568             :                     {
    9569          19 :                         osModDefinition += osDefinition[k];
    9570          38 :                         if (!((osDefinition[k] >= '0' &&
    9571          16 :                                osDefinition[k] <= '9') ||
    9572          15 :                               osDefinition[k] == '.'))
    9573          12 :                             break;
    9574             :                     }
    9575          24 :                     if (k == osDefinition.size() ||
    9576          12 :                         (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
    9577           5 :                          osDefinition[k] != 's'))
    9578             :                     {
    9579           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9580             :                                  "Value of "
    9581             :                                  "BAND_METADATA[%d][\"item_value\"] = "
    9582             :                                  "%s is invalid at offset %d: only "
    9583             :                                  "%%[x][.y]f|g or %%s formatters are accepted",
    9584             :                                  j, osDefinition.c_str(), int(k));
    9585           1 :                         return nullptr;
    9586             :                     }
    9587          11 :                     bDefinitionUsesPctForG =
    9588          11 :                         (osDefinition[k] == 'f' || osDefinition[k] == 'g');
    9589          11 :                     if (bDefinitionUsesPctForG)
    9590             :                     {
    9591          12 :                         if (poArray &&
    9592          12 :                             poArray->GetDataType().GetClass() != GEDTC_NUMERIC)
    9593             :                         {
    9594           1 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9595             :                                      "Data type of %s array is not numeric",
    9596           1 :                                      poArray->GetName().c_str());
    9597           1 :                             return nullptr;
    9598             :                         }
    9599           8 :                         else if (poAttribute &&
    9600           2 :                                  poAttribute->GetDataType().GetClass() !=
    9601           6 :                                      GEDTC_NUMERIC)
    9602             :                         {
    9603           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9604             :                                      "Data type of %s attribute is not numeric",
    9605           0 :                                      poAttribute->GetFullName().c_str());
    9606           0 :                             return nullptr;
    9607             :                         }
    9608             :                     }
    9609             :                 }
    9610          62 :                 else if (osDefinition[k] == '$' &&
    9611          62 :                          k + 1 < osDefinition.size() &&
    9612           7 :                          osDefinition[k + 1] == '{')
    9613             :                 {
    9614           7 :                     const auto nPos = osDefinition.find('}', k);
    9615           7 :                     if (nPos == std::string::npos)
    9616             :                     {
    9617           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9618             :                                  "Value of "
    9619             :                                  "BAND_METADATA[%d][\"item_value\"] = "
    9620             :                                  "%s is invalid at offset %d",
    9621             :                                  j, osDefinition.c_str(), int(k));
    9622           3 :                         return nullptr;
    9623             :                     }
    9624             :                     const auto osAttrName =
    9625           6 :                         osDefinition.substr(k + 2, nPos - (k + 2));
    9626           0 :                     std::shared_ptr<GDALAttribute> poAttr;
    9627           6 :                     if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
    9628             :                     {
    9629           4 :                         poAttr = poArray->GetAttribute(osAttrName);
    9630           4 :                         if (!poAttr)
    9631             :                         {
    9632           1 :                             CPLError(
    9633             :                                 CE_Failure, CPLE_AppDefined,
    9634             :                                 "Value of "
    9635             :                                 "BAND_METADATA[%d][\"item_value\"] = "
    9636             :                                 "%s is invalid: %s is not an attribute of %s",
    9637             :                                 j, osDefinition.c_str(), osAttrName.c_str(),
    9638           1 :                                 poArray->GetName().c_str());
    9639           1 :                             return nullptr;
    9640             :                         }
    9641             :                     }
    9642             :                     else
    9643             :                     {
    9644             :                         poAttr =
    9645           2 :                             poRootGroup->OpenAttributeFromFullname(osAttrName);
    9646           2 :                         if (!poAttr)
    9647             :                         {
    9648           1 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9649             :                                      "Value of "
    9650             :                                      "BAND_METADATA[%d][\"item_value\"] = "
    9651             :                                      "%s is invalid: %s is not an attribute",
    9652             :                                      j, osDefinition.c_str(),
    9653             :                                      osAttrName.c_str());
    9654           1 :                             return nullptr;
    9655             :                         }
    9656             :                     }
    9657           4 :                     k = nPos;
    9658           4 :                     const char *pszValue = poAttr->ReadAsString();
    9659           4 :                     if (!pszValue)
    9660             :                     {
    9661           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9662             :                                  "Cannot get value of attribute %s as a "
    9663             :                                  "string",
    9664             :                                  osAttrName.c_str());
    9665           0 :                         return nullptr;
    9666             :                     }
    9667           4 :                     osModDefinition += pszValue;
    9668             :                 }
    9669             :                 else
    9670             :                 {
    9671          48 :                     osModDefinition += osDefinition[k];
    9672             :                 }
    9673             :             }
    9674             : 
    9675           9 :             if (poArray)
    9676           8 :                 oItem.poArray = std::move(poArray);
    9677             :             else
    9678           1 :                 oItem.poArray = std::move(poAttribute);
    9679           9 :             oItem.osDefinition = std::move(osModDefinition);
    9680           9 :             oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
    9681             : 
    9682           9 :             aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
    9683           9 :                 std::move(oItem));
    9684             :         }
    9685             :     }
    9686             : 
    9687         518 :     std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
    9688             :     const char *pszBandImageryMetadata =
    9689         259 :         CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
    9690         259 :     if (pszBandImageryMetadata)
    9691             :     {
    9692          20 :         if (!poRootGroup)
    9693             :         {
    9694           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9695             :                      "Root group should be provided when BAND_IMAGERY_METADATA "
    9696             :                      "is set");
    9697          17 :             return nullptr;
    9698             :         }
    9699          19 :         CPLJSONDocument oDoc;
    9700          19 :         if (!oDoc.LoadMemory(pszBandImageryMetadata))
    9701             :         {
    9702           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9703             :                      "Invalid JSON content for BAND_IMAGERY_METADATA");
    9704           1 :             return nullptr;
    9705             :         }
    9706          18 :         auto oRoot = oDoc.GetRoot();
    9707          18 :         if (oRoot.GetType() != CPLJSONObject::Type::Object)
    9708             :         {
    9709           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9710             :                      "Value of BAND_IMAGERY_METADATA should be an object");
    9711           1 :             return nullptr;
    9712             :         }
    9713          21 :         for (const auto &oJsonItem : oRoot.GetChildren())
    9714             :         {
    9715          38 :             if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
    9716          20 :                 oJsonItem.GetName() == "FWHM_UM")
    9717             :             {
    9718          34 :                 const auto osBandArrayFullname = oJsonItem.GetString("array");
    9719             :                 const auto osBandAttributeName =
    9720          34 :                     oJsonItem.GetString("attribute");
    9721           0 :                 std::shared_ptr<GDALMDArray> poArray;
    9722           0 :                 std::shared_ptr<GDALAttribute> poAttribute;
    9723          17 :                 size_t iExtraDimIdx = 0;
    9724          17 :                 if (osBandArrayFullname.empty() && osBandAttributeName.empty())
    9725             :                 {
    9726           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9727             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] or "
    9728             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] is "
    9729             :                              "missing",
    9730           2 :                              oJsonItem.GetName().c_str(),
    9731           2 :                              oJsonItem.GetName().c_str());
    9732           1 :                     return nullptr;
    9733             :                 }
    9734          25 :                 else if (!osBandArrayFullname.empty() &&
    9735           9 :                          !osBandAttributeName.empty())
    9736             :                 {
    9737           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9738             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] and "
    9739             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] are "
    9740             :                              "mutually exclusive",
    9741           2 :                              oJsonItem.GetName().c_str(),
    9742           2 :                              oJsonItem.GetName().c_str());
    9743           1 :                     return nullptr;
    9744             :                 }
    9745          15 :                 else if (!osBandArrayFullname.empty())
    9746             :                 {
    9747          16 :                     poArray = poRootGroup->OpenMDArrayFromFullname(
    9748           8 :                         osBandArrayFullname);
    9749           8 :                     if (!poArray)
    9750             :                     {
    9751           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9752             :                                  "Array %s cannot be found",
    9753             :                                  osBandArrayFullname.c_str());
    9754           3 :                         return nullptr;
    9755             :                     }
    9756           7 :                     if (poArray->GetDimensionCount() != 1)
    9757             :                     {
    9758           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9759             :                                  "Array %s is not a 1D array",
    9760             :                                  osBandArrayFullname.c_str());
    9761           1 :                         return nullptr;
    9762             :                     }
    9763             :                     const auto &osAuxArrayDimName =
    9764           6 :                         poArray->GetDimensions()[0]->GetName();
    9765             :                     auto oIter =
    9766           6 :                         oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
    9767           6 :                     if (oIter == oMapArrayDimNameToExtraDimIdx.end())
    9768             :                     {
    9769           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9770             :                                  "Dimension \"%s\" of array \"%s\" is not a "
    9771             :                                  "non-X/Y dimension of array \"%s\"",
    9772             :                                  osAuxArrayDimName.c_str(),
    9773             :                                  osBandArrayFullname.c_str(),
    9774           1 :                                  array->GetName().c_str());
    9775           1 :                         return nullptr;
    9776             :                     }
    9777           5 :                     iExtraDimIdx = oIter->second;
    9778           5 :                     CPLAssert(iExtraDimIdx < nNewDimCount);
    9779             :                 }
    9780             :                 else
    9781             :                 {
    9782             :                     poAttribute =
    9783           7 :                         !osBandAttributeName.empty() &&
    9784           7 :                                 osBandAttributeName[0] == '/'
    9785          16 :                             ? poRootGroup->OpenAttributeFromFullname(
    9786             :                                   osBandAttributeName)
    9787           7 :                             : array->GetAttribute(osBandAttributeName);
    9788           7 :                     if (!poAttribute)
    9789             :                     {
    9790           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9791             :                                  "Attribute %s cannot be found",
    9792             :                                  osBandAttributeName.c_str());
    9793           6 :                         return nullptr;
    9794             :                     }
    9795           5 :                     const auto aoAttrDims = poAttribute->GetDimensionsSize();
    9796           5 :                     if (aoAttrDims.size() != 1)
    9797             :                     {
    9798           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9799             :                                  "Attribute %s is not a 1D array",
    9800             :                                  osBandAttributeName.c_str());
    9801           2 :                         return nullptr;
    9802             :                     }
    9803           3 :                     bool found = false;
    9804           6 :                     for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
    9805             :                     {
    9806           4 :                         if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
    9807           4 :                                 ->GetSize() == aoAttrDims[0])
    9808             :                         {
    9809           3 :                             if (found)
    9810             :                             {
    9811           2 :                                 CPLError(CE_Failure, CPLE_AppDefined,
    9812             :                                          "Several dimensions of %s have the "
    9813             :                                          "same size as attribute %s. Cannot "
    9814             :                                          "infer which one to bind to!",
    9815           1 :                                          array->GetName().c_str(),
    9816             :                                          osBandAttributeName.c_str());
    9817           1 :                                 return nullptr;
    9818             :                             }
    9819           2 :                             found = true;
    9820           2 :                             iExtraDimIdx = iter.second;
    9821             :                         }
    9822             :                     }
    9823           2 :                     if (!found)
    9824             :                     {
    9825           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9826             :                                  "No dimension of %s has the same size as "
    9827             :                                  "attribute %s",
    9828           1 :                                  array->GetName().c_str(),
    9829             :                                  osBandAttributeName.c_str());
    9830           1 :                         return nullptr;
    9831             :                     }
    9832             :                 }
    9833             : 
    9834          12 :                 std::string osUnit = oJsonItem.GetString("unit", "um");
    9835           6 :                 if (STARTS_WITH(osUnit.c_str(), "${"))
    9836             :                 {
    9837           4 :                     if (osUnit.back() != '}')
    9838             :                     {
    9839           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9840             :                                  "Value of "
    9841             :                                  "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
    9842             :                                  "%s is invalid",
    9843           2 :                                  oJsonItem.GetName().c_str(), osUnit.c_str());
    9844           2 :                         return nullptr;
    9845             :                     }
    9846           3 :                     const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
    9847           0 :                     std::shared_ptr<GDALAttribute> poAttr;
    9848           3 :                     if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
    9849             :                     {
    9850           2 :                         poAttr = poArray->GetAttribute(osAttrName);
    9851           2 :                         if (!poAttr)
    9852             :                         {
    9853           2 :                             CPLError(
    9854             :                                 CE_Failure, CPLE_AppDefined,
    9855             :                                 "Value of "
    9856             :                                 "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
    9857             :                                 "%s is invalid: %s is not an attribute of %s",
    9858           2 :                                 oJsonItem.GetName().c_str(), osUnit.c_str(),
    9859             :                                 osAttrName.c_str(),
    9860             :                                 osBandArrayFullname.c_str());
    9861           1 :                             return nullptr;
    9862             :                         }
    9863             :                     }
    9864             :                     else
    9865             :                     {
    9866             :                         poAttr =
    9867           1 :                             poRootGroup->OpenAttributeFromFullname(osAttrName);
    9868           1 :                         if (!poAttr)
    9869             :                         {
    9870           0 :                             CPLError(
    9871             :                                 CE_Failure, CPLE_AppDefined,
    9872             :                                 "Value of "
    9873             :                                 "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
    9874             :                                 "%s is invalid: %s is not an attribute",
    9875           0 :                                 oJsonItem.GetName().c_str(), osUnit.c_str(),
    9876             :                                 osAttrName.c_str());
    9877           0 :                             return nullptr;
    9878             :                         }
    9879             :                     }
    9880             : 
    9881           2 :                     const char *pszValue = poAttr->ReadAsString();
    9882           2 :                     if (!pszValue)
    9883             :                     {
    9884           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9885             :                                  "Cannot get value of attribute %s of %s as a "
    9886             :                                  "string",
    9887             :                                  osAttrName.c_str(),
    9888             :                                  osBandArrayFullname.c_str());
    9889           0 :                         return nullptr;
    9890             :                     }
    9891           2 :                     osUnit = pszValue;
    9892             :                 }
    9893           4 :                 double dfConvToUM = 1.0;
    9894          10 :                 if (osUnit == "nm" || osUnit == "nanometre" ||
    9895          13 :                     osUnit == "nanometres" || osUnit == "nanometer" ||
    9896           3 :                     osUnit == "nanometers")
    9897             :                 {
    9898           1 :                     dfConvToUM = 1e-3;
    9899             :                 }
    9900           5 :                 else if (!(osUnit == "um" || osUnit == "micrometre" ||
    9901           2 :                            osUnit == "micrometres" || osUnit == "micrometer" ||
    9902           1 :                            osUnit == "micrometers"))
    9903             :                 {
    9904           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9905             :                              "Unhandled value for "
    9906             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
    9907           2 :                              oJsonItem.GetName().c_str(), osUnit.c_str());
    9908           1 :                     return nullptr;
    9909             :                 }
    9910             : 
    9911           3 :                 BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
    9912             : 
    9913           3 :                 std::shared_ptr<GDALAbstractMDArray> abstractArray;
    9914           3 :                 if (poArray)
    9915           2 :                     abstractArray = std::move(poArray);
    9916             :                 else
    9917           1 :                     abstractArray = std::move(poAttribute);
    9918           3 :                 if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
    9919             :                 {
    9920           2 :                     item.poCentralWavelengthArray = std::move(abstractArray);
    9921           2 :                     item.dfCentralWavelengthToMicrometer = dfConvToUM;
    9922             :                 }
    9923             :                 else
    9924             :                 {
    9925           1 :                     item.poFWHMArray = std::move(abstractArray);
    9926           1 :                     item.dfFWHMToMicrometer = dfConvToUM;
    9927             :                 }
    9928             :             }
    9929             :             else
    9930             :             {
    9931           1 :                 CPLError(CE_Warning, CPLE_AppDefined,
    9932             :                          "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
    9933           2 :                          oJsonItem.GetName().c_str());
    9934             :             }
    9935             :         }
    9936             :     }
    9937             : 
    9938         484 :     auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
    9939             : 
    9940         242 :     poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
    9941             : 
    9942         242 :     poDS->nRasterYSize =
    9943         242 :         nDimCount < 2 ? 1
    9944         226 :                       : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    9945         226 :                                                   dims[iYDim]->GetSize()));
    9946         484 :     poDS->nRasterXSize = static_cast<int>(
    9947         242 :         std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
    9948             : 
    9949         484 :     std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
    9950         484 :     std::vector<GUInt64> anStackIters(nDimCount);
    9951         484 :     std::vector<size_t> anMapNewToOld(nNewDimCount);
    9952         760 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    9953             :     {
    9954         518 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9955             :         {
    9956          50 :             anMapNewToOld[j] = i;
    9957          50 :             j++;
    9958             :         }
    9959             :     }
    9960             : 
    9961         242 :     poDS->m_bHasGT = array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_gt);
    9962             : 
    9963         484 :     const auto attrs(array->GetAttributes());
    9964         387 :     for (const auto &attr : attrs)
    9965             :     {
    9966         145 :         if (attr->GetName() != "COLOR_INTERPRETATION")
    9967             :         {
    9968         268 :             auto stringArray = attr->ReadAsStringArray();
    9969         268 :             std::string val;
    9970         134 :             if (stringArray.size() > 1)
    9971             :             {
    9972          57 :                 val += '{';
    9973             :             }
    9974         547 :             for (int i = 0; i < stringArray.size(); ++i)
    9975             :             {
    9976         413 :                 if (i > 0)
    9977         279 :                     val += ',';
    9978         413 :                 val += stringArray[i];
    9979             :             }
    9980         134 :             if (stringArray.size() > 1)
    9981             :             {
    9982          57 :                 val += '}';
    9983             :             }
    9984         134 :             poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
    9985             :         }
    9986             :     }
    9987             : 
    9988         242 :     const char *pszDelay = CSLFetchNameValueDef(
    9989             :         papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
    9990             :         CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
    9991             :     const double dfDelay =
    9992         242 :         EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
    9993         242 :     const auto nStartTime = time(nullptr);
    9994         242 :     bool bHasWarned = false;
    9995             :     // Instantiate bands by iterating over non-XY variables
    9996         242 :     size_t iDim = 0;
    9997         242 :     int nCurBand = 1;
    9998         370 : lbl_next_depth:
    9999         370 :     if (iDim < nNewDimCount)
   10000             :     {
   10001          52 :         anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
   10002          52 :         anOtherDimCoord[iDim] = 0;
   10003             :         while (true)
   10004             :         {
   10005         128 :             ++iDim;
   10006         128 :             goto lbl_next_depth;
   10007         128 :         lbl_return_to_caller:
   10008         128 :             --iDim;
   10009         128 :             --anStackIters[iDim];
   10010         128 :             if (anStackIters[iDim] == 0)
   10011          52 :                 break;
   10012          76 :             ++anOtherDimCoord[iDim];
   10013             :         }
   10014             :     }
   10015             :     else
   10016             :     {
   10017         636 :         poDS->SetBand(nCurBand,
   10018             :                       new GDALRasterBandFromArray(
   10019         318 :                           poDS.get(), anOtherDimCoord,
   10020             :                           aoBandParameterMetadataItems, aoBandImageryMetadata,
   10021         318 :                           dfDelay, nStartTime, bHasWarned));
   10022         318 :         ++nCurBand;
   10023             :     }
   10024         370 :     if (iDim > 0)
   10025         128 :         goto lbl_return_to_caller;
   10026             : 
   10027         242 :     if (!array->GetFilename().empty())
   10028             :     {
   10029         205 :         poDS->SetPhysicalFilename(array->GetFilename().c_str());
   10030             :         std::string osDerivedDatasetName(
   10031             :             CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
   10032         410 :                        int(iYDim), array->GetFullName().c_str()));
   10033         205 :         if (!array->GetContext().empty())
   10034             :         {
   10035           2 :             osDerivedDatasetName += " with context ";
   10036           2 :             osDerivedDatasetName += array->GetContext();
   10037             :         }
   10038         205 :         poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
   10039         205 :         poDS->TryLoadXML();
   10040             : 
   10041           2 :         for (const auto &[pszKey, pszValue] :
   10042             :              cpl::IterateNameValue(static_cast<CSLConstList>(
   10043         207 :                  poDS->GDALPamDataset::GetMetadata())))
   10044             :         {
   10045           1 :             poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
   10046             :         }
   10047             :     }
   10048             : 
   10049         242 :     return poDS.release();
   10050             : }
   10051             : 
   10052             : /************************************************************************/
   10053             : /*                          AsClassicDataset()                         */
   10054             : /************************************************************************/
   10055             : 
   10056             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   10057             :  *
   10058             :  * In the case of > 2D arrays, additional dimensions will be represented as
   10059             :  * raster bands.
   10060             :  *
   10061             :  * The "reverse" method is GDALRasterBand::AsMDArray().
   10062             :  *
   10063             :  * This is the same as the C function GDALMDArrayAsClassicDataset().
   10064             :  *
   10065             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   10066             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   10067             :  *              Ignored if the dimension count is 1.
   10068             :  * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
   10069             :  *                    and BAND_IMAGERY_METADATA option.
   10070             :  * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
   10071             :  *                     nullptr. Current supported options are:
   10072             :  *                     <ul>
   10073             :  *                     <li>BAND_METADATA: JSON serialized array defining which
   10074             :  *                         arrays of the poRootGroup, indexed by non-X and Y
   10075             :  *                         dimensions, should be mapped as band metadata items.
   10076             :  *                         Each array item should be an object with the
   10077             :  *                         following members:
   10078             :  *                         - "array": full name of a band parameter array.
   10079             :  *                           Such array must be a one
   10080             :  *                           dimensional array, and its dimension must be one of
   10081             :  *                           the dimensions of the array on which the method is
   10082             :  *                           called (excluding the X and Y dimensions).
   10083             :  *                         - "attribute": name relative to *this array or full
   10084             :  *                           name of a single dimension numeric array whose size
   10085             :  *                           must be one of the dimensions of *this array
   10086             :  *                           (excluding the X and Y dimensions).
   10087             :  *                           "array" and "attribute" are mutually exclusive.
   10088             :  *                         - "item_name": band metadata item name
   10089             :  *                         - "item_value": (optional) String, where "%[x][.y]f",
   10090             :  *                           "%[x][.y]g" or "%s" printf-like formatting can be
   10091             :  *                           used to format the corresponding value of the
   10092             :  *                           parameter array. The percentage character should be
   10093             :  *                           repeated: "%%"
   10094             :  *                           "${attribute_name}" can also be used to include the
   10095             :  *                           value of an attribute for "array" when set and if
   10096             :  *                           not starting with '/'. Otherwise if starting with
   10097             :  *                           '/', it is the full path to the attribute.
   10098             :  *
   10099             :  *                           If "item_value" is not provided, a default formatting
   10100             :  *                           of the value will be applied.
   10101             :  *
   10102             :  *                         Example:
   10103             :  *                         [
   10104             :  *                            {
   10105             :  *                              "array": "/sensor_band_parameters/wavelengths",
   10106             :  *                              "item_name": "WAVELENGTH",
   10107             :  *                              "item_value": "%.1f ${units}"
   10108             :  *                            },
   10109             :  *                            {
   10110             :  *                              "array": "/sensor_band_parameters/fwhm",
   10111             :  *                              "item_name": "FWHM"
   10112             :  *                            },
   10113             :  *                            {
   10114             :  *                              "array": "/sensor_band_parameters/fwhm",
   10115             :  *                              "item_name": "FWHM_UNIT",
   10116             :  *                              "item_value": "${units}"
   10117             :  *                            }
   10118             :  *                         ]
   10119             :  *
   10120             :  *                         Example for Planet Labs Tanager radiance products:
   10121             :  *                         [
   10122             :  *                            {
   10123             :  *                              "attribute": "center_wavelengths",
   10124             :  *                              "item_name": "WAVELENGTH",
   10125             :  *                              "item_value": "%.1f ${center_wavelengths_units}"
   10126             :  *                            }
   10127             :  *                         ]
   10128             :  *
   10129             :  *                     </li>
   10130             :  *                     <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
   10131             :  *                         JSON serialized object defining which arrays of the
   10132             :  *                         poRootGroup, indexed by non-X and Y dimensions,
   10133             :  *                         should be mapped as band metadata items in the
   10134             :  *                         band IMAGERY domain.
   10135             :  *                         The object currently accepts 2 members:
   10136             :  *                         - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
   10137             :  *                           micrometers.
   10138             :  *                         - "FWHM_UM": Full-width half-maximum
   10139             :  *                           in micrometers.
   10140             :  *                         The value of each member should be an object with the
   10141             :  *                         following members:
   10142             :  *                         - "array": full name of a band parameter array.
   10143             :  *                           Such array must be a one dimensional array, and its
   10144             :  *                           dimension must be one of the dimensions of the
   10145             :  *                           array on which the method is called
   10146             :  *                           (excluding the X and Y dimensions).
   10147             :  *                         - "attribute": name relative to *this array or full
   10148             :  *                           name of a single dimension numeric array whose size
   10149             :  *                           must be one of the dimensions of *this array
   10150             :  *                           (excluding the X and Y dimensions).
   10151             :  *                           "array" and "attribute" are mutually exclusive,
   10152             :  *                           and one of them is required.
   10153             :  *                         - "unit": (optional) unit of the values pointed in
   10154             :  *                           the above array.
   10155             :  *                           Can be a literal string or a string of the form
   10156             :  *                           "${attribute_name}" to point to an attribute for
   10157             :  *                           "array" when set and if no starting
   10158             :  *                           with '/'. Otherwise if starting with '/', it is
   10159             :  *                           the full path to the attribute.
   10160             :  *                           Accepted values are "um", "micrometer"
   10161             :  *                           (with UK vs US spelling, singular or plural), "nm",
   10162             :  *                           "nanometer" (with UK vs US spelling, singular or
   10163             :  *                           plural)
   10164             :  *                           If not provided, micrometer is assumed.
   10165             :  *
   10166             :  *                         Example for EMIT datasets:
   10167             :  *                         {
   10168             :  *                            "CENTRAL_WAVELENGTH_UM": {
   10169             :  *                                "array": "/sensor_band_parameters/wavelengths",
   10170             :  *                                "unit": "${units}"
   10171             :  *                            },
   10172             :  *                            "FWHM_UM": {
   10173             :  *                                "array": "/sensor_band_parameters/fwhm",
   10174             :  *                                "unit": "${units}"
   10175             :  *                            }
   10176             :  *                         }
   10177             :  *
   10178             :  *                         Example for Planet Labs Tanager radiance products:
   10179             :  *                         {
   10180             :  *                            "CENTRAL_WAVELENGTH_UM": {
   10181             :  *                              "attribute": "center_wavelengths",
   10182             :  *                              "unit": "${center_wavelengths_units}"
   10183             :  *                            },
   10184             :  *                            "FWHM_UM": {
   10185             :  *                              "attribute": "fwhm",
   10186             :  *                              "unit": "${fwhm_units}"
   10187             :  *                            }
   10188             :  *                         }
   10189             :  *
   10190             :  *                     </li>
   10191             :  *                     <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
   10192             :  *                         seconds allowed to set the DIM_{dimname}_VALUE band
   10193             :  *                         metadata items from the indexing variable of the
   10194             :  *                         dimensions.
   10195             :  *                         Default value is 5. 'unlimited' can be used to mean
   10196             :  *                         unlimited delay. Can also be defined globally with
   10197             :  *                         the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
   10198             :  *                         option.</li>
   10199             :  *                     </ul>
   10200             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   10201             :  */
   10202             : GDALDataset *
   10203         294 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
   10204             :                               const std::shared_ptr<GDALGroup> &poRootGroup,
   10205             :                               CSLConstList papszOptions) const
   10206             : {
   10207         588 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
   10208         294 :     if (!self)
   10209             :     {
   10210           0 :         CPLError(CE_Failure, CPLE_AppDefined,
   10211             :                  "Driver implementation issue: m_pSelf not set !");
   10212           0 :         return nullptr;
   10213             :     }
   10214         294 :     return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
   10215         294 :                                         papszOptions);
   10216             : }
   10217             : 
   10218             : /************************************************************************/
   10219             : /*                           GetStatistics()                            */
   10220             : /************************************************************************/
   10221             : 
   10222             : /**
   10223             :  * \brief Fetch statistics.
   10224             :  *
   10225             :  * Returns the minimum, maximum, mean and standard deviation of all
   10226             :  * pixel values in this array.
   10227             :  *
   10228             :  * If bForce is FALSE results will only be returned if it can be done
   10229             :  * quickly (i.e. without scanning the data).  If bForce is FALSE and
   10230             :  * results cannot be returned efficiently, the method will return CE_Warning
   10231             :  * but no warning will have been issued.   This is a non-standard use of
   10232             :  * the CE_Warning return value to indicate "nothing done".
   10233             :  *
   10234             :  * When cached statistics are not available, and bForce is TRUE,
   10235             :  * ComputeStatistics() is called.
   10236             :  *
   10237             :  * Note that file formats using PAM (Persistent Auxiliary Metadata) services
   10238             :  * will generally cache statistics in the .aux.xml file allowing fast fetch
   10239             :  * after the first request.
   10240             :  *
   10241             :  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
   10242             :  *
   10243             :  * This method is the same as the C function GDALMDArrayGetStatistics().
   10244             :  *
   10245             :  * @param bApproxOK Currently ignored. In the future, should be set to true
   10246             :  * if statistics on the whole array are wished, or to false if a subset of it
   10247             :  * may be used.
   10248             :  *
   10249             :  * @param bForce If false statistics will only be returned if it can
   10250             :  * be done without rescanning the image.
   10251             :  *
   10252             :  * @param pdfMin Location into which to load image minimum (may be NULL).
   10253             :  *
   10254             :  * @param pdfMax Location into which to load image maximum (may be NULL).-
   10255             :  *
   10256             :  * @param pdfMean Location into which to load image mean (may be NULL).
   10257             :  *
   10258             :  * @param pdfStdDev Location into which to load image standard deviation
   10259             :  * (may be NULL).
   10260             :  *
   10261             :  * @param pnValidCount Number of samples whose value is different from the
   10262             :  * nodata value. (may be NULL)
   10263             :  *
   10264             :  * @param pfnProgress a function to call to report progress, or NULL.
   10265             :  *
   10266             :  * @param pProgressData application data to pass to the progress function.
   10267             :  *
   10268             :  * @return CE_None on success, CE_Warning if no values returned,
   10269             :  * CE_Failure if an error occurs.
   10270             :  *
   10271             :  * @since GDAL 3.2
   10272             :  */
   10273             : 
   10274          10 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
   10275             :                                   double *pdfMax, double *pdfMean,
   10276             :                                   double *pdfStdDev, GUInt64 *pnValidCount,
   10277             :                                   GDALProgressFunc pfnProgress,
   10278             :                                   void *pProgressData)
   10279             : {
   10280          10 :     if (!bForce)
   10281           1 :         return CE_Warning;
   10282             : 
   10283          18 :     return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
   10284           9 :                              pnValidCount, pfnProgress, pProgressData, nullptr)
   10285           9 :                ? CE_None
   10286           9 :                : CE_Failure;
   10287             : }
   10288             : 
   10289             : /************************************************************************/
   10290             : /*                         ComputeStatistics()                          */
   10291             : /************************************************************************/
   10292             : 
   10293             : /**
   10294             :  * \brief Compute statistics.
   10295             :  *
   10296             :  * Returns the minimum, maximum, mean and standard deviation of all
   10297             :  * pixel values in this array.
   10298             :  *
   10299             :  * Pixels taken into account in statistics are those whose mask value
   10300             :  * (as determined by GetMask()) is non-zero.
   10301             :  *
   10302             :  * Once computed, the statistics will generally be "set" back on the
   10303             :  * owing dataset.
   10304             :  *
   10305             :  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
   10306             :  *
   10307             :  * This method is the same as the C functions GDALMDArrayComputeStatistics().
   10308             :  * and GDALMDArrayComputeStatisticsEx().
   10309             :  *
   10310             :  * @param bApproxOK Currently ignored. In the future, should be set to true
   10311             :  * if statistics on the whole array are wished, or to false if a subset of it
   10312             :  * may be used.
   10313             :  *
   10314             :  * @param pdfMin Location into which to load image minimum (may be NULL).
   10315             :  *
   10316             :  * @param pdfMax Location into which to load image maximum (may be NULL).-
   10317             :  *
   10318             :  * @param pdfMean Location into which to load image mean (may be NULL).
   10319             :  *
   10320             :  * @param pdfStdDev Location into which to load image standard deviation
   10321             :  * (may be NULL).
   10322             :  *
   10323             :  * @param pnValidCount Number of samples whose value is different from the
   10324             :  * nodata value. (may be NULL)
   10325             :  *
   10326             :  * @param pfnProgress a function to call to report progress, or NULL.
   10327             :  *
   10328             :  * @param pProgressData application data to pass to the progress function.
   10329             :  *
   10330             :  * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
   10331             :  *                     Options are driver specific. For now the netCDF and Zarr
   10332             :  *                     drivers recognize UPDATE_METADATA=YES, whose effect is
   10333             :  *                     to add or update the actual_range attribute with the
   10334             :  *                     computed min/max, only if done on the full array, in non
   10335             :  *                     approximate mode, and the dataset is opened in update
   10336             :  *                     mode.
   10337             :  *
   10338             :  * @return true on success
   10339             :  *
   10340             :  * @since GDAL 3.2
   10341             :  */
   10342             : 
   10343          13 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
   10344             :                                     double *pdfMax, double *pdfMean,
   10345             :                                     double *pdfStdDev, GUInt64 *pnValidCount,
   10346             :                                     GDALProgressFunc pfnProgress,
   10347             :                                     void *pProgressData,
   10348             :                                     CSLConstList papszOptions)
   10349             : {
   10350             :     struct StatsPerChunkType
   10351             :     {
   10352             :         const GDALMDArray *array = nullptr;
   10353             :         std::shared_ptr<GDALMDArray> poMask{};
   10354             :         double dfMin = cpl::NumericLimits<double>::max();
   10355             :         double dfMax = -cpl::NumericLimits<double>::max();
   10356             :         double dfMean = 0.0;
   10357             :         double dfM2 = 0.0;
   10358             :         GUInt64 nValidCount = 0;
   10359             :         std::vector<GByte> abyData{};
   10360             :         std::vector<double> adfData{};
   10361             :         std::vector<GByte> abyMaskData{};
   10362             :         GDALProgressFunc pfnProgress = nullptr;
   10363             :         void *pProgressData = nullptr;
   10364             :     };
   10365             : 
   10366          13 :     const auto PerChunkFunc = [](GDALAbstractMDArray *,
   10367             :                                  const GUInt64 *chunkArrayStartIdx,
   10368             :                                  const size_t *chunkCount, GUInt64 iCurChunk,
   10369             :                                  GUInt64 nChunkCount, void *pUserData)
   10370             :     {
   10371          13 :         StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
   10372          13 :         const GDALMDArray *array = data->array;
   10373          13 :         const GDALMDArray *poMask = data->poMask.get();
   10374          13 :         const size_t nDims = array->GetDimensionCount();
   10375          13 :         size_t nVals = 1;
   10376          34 :         for (size_t i = 0; i < nDims; i++)
   10377          21 :             nVals *= chunkCount[i];
   10378             : 
   10379             :         // Get mask
   10380          13 :         data->abyMaskData.resize(nVals);
   10381          13 :         if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
   10382          13 :                            poMask->GetDataType(), &data->abyMaskData[0])))
   10383             :         {
   10384           0 :             return false;
   10385             :         }
   10386             : 
   10387             :         // Get data
   10388          13 :         const auto &oType = array->GetDataType();
   10389          13 :         if (oType.GetNumericDataType() == GDT_Float64)
   10390             :         {
   10391           6 :             data->adfData.resize(nVals);
   10392           6 :             if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
   10393           6 :                              oType, &data->adfData[0]))
   10394             :             {
   10395           0 :                 return false;
   10396             :             }
   10397             :         }
   10398             :         else
   10399             :         {
   10400           7 :             data->abyData.resize(nVals * oType.GetSize());
   10401           7 :             if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
   10402           7 :                              oType, &data->abyData[0]))
   10403             :             {
   10404           0 :                 return false;
   10405             :             }
   10406           7 :             data->adfData.resize(nVals);
   10407           7 :             GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
   10408           7 :                             static_cast<int>(oType.GetSize()),
   10409           7 :                             &data->adfData[0], GDT_Float64,
   10410             :                             static_cast<int>(sizeof(double)),
   10411             :                             static_cast<GPtrDiff_t>(nVals));
   10412             :         }
   10413         912 :         for (size_t i = 0; i < nVals; i++)
   10414             :         {
   10415         899 :             if (data->abyMaskData[i])
   10416             :             {
   10417         894 :                 const double dfValue = data->adfData[i];
   10418         894 :                 data->dfMin = std::min(data->dfMin, dfValue);
   10419         894 :                 data->dfMax = std::max(data->dfMax, dfValue);
   10420         894 :                 data->nValidCount++;
   10421         894 :                 const double dfDelta = dfValue - data->dfMean;
   10422         894 :                 data->dfMean += dfDelta / data->nValidCount;
   10423         894 :                 data->dfM2 += dfDelta * (dfValue - data->dfMean);
   10424             :             }
   10425             :         }
   10426          13 :         if (data->pfnProgress &&
   10427           0 :             !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
   10428             :                                "", data->pProgressData))
   10429             :         {
   10430           0 :             return false;
   10431             :         }
   10432          13 :         return true;
   10433             :     };
   10434             : 
   10435          13 :     const auto &oType = GetDataType();
   10436          26 :     if (oType.GetClass() != GEDTC_NUMERIC ||
   10437          13 :         GDALDataTypeIsComplex(oType.GetNumericDataType()))
   10438             :     {
   10439           0 :         CPLError(
   10440             :             CE_Failure, CPLE_NotSupported,
   10441             :             "Statistics can only be computed on non-complex numeric data type");
   10442           0 :         return false;
   10443             :     }
   10444             : 
   10445          13 :     const size_t nDims = GetDimensionCount();
   10446          26 :     std::vector<GUInt64> arrayStartIdx(nDims);
   10447          26 :     std::vector<GUInt64> count(nDims);
   10448          13 :     const auto &poDims = GetDimensions();
   10449          34 :     for (size_t i = 0; i < nDims; i++)
   10450             :     {
   10451          21 :         count[i] = poDims[i]->GetSize();
   10452             :     }
   10453          13 :     const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
   10454             :     const size_t nMaxChunkSize =
   10455             :         pszSwathSize
   10456          13 :             ? static_cast<size_t>(
   10457           0 :                   std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
   10458           0 :                            CPLAtoGIntBig(pszSwathSize)))
   10459             :             : static_cast<size_t>(
   10460          13 :                   std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
   10461          13 :                            GDALGetCacheMax64() / 4));
   10462          26 :     StatsPerChunkType sData;
   10463          13 :     sData.array = this;
   10464          13 :     sData.poMask = GetMask(nullptr);
   10465          13 :     if (sData.poMask == nullptr)
   10466             :     {
   10467           0 :         return false;
   10468             :     }
   10469          13 :     sData.pfnProgress = pfnProgress;
   10470          13 :     sData.pProgressData = pProgressData;
   10471          13 :     if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
   10472          26 :                          GetProcessingChunkSize(nMaxChunkSize).data(),
   10473          13 :                          PerChunkFunc, &sData))
   10474             :     {
   10475           0 :         return false;
   10476             :     }
   10477             : 
   10478          13 :     if (pdfMin)
   10479          13 :         *pdfMin = sData.dfMin;
   10480             : 
   10481          13 :     if (pdfMax)
   10482          13 :         *pdfMax = sData.dfMax;
   10483             : 
   10484          13 :     if (pdfMean)
   10485          11 :         *pdfMean = sData.dfMean;
   10486             : 
   10487             :     const double dfStdDev =
   10488          13 :         sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
   10489          13 :     if (pdfStdDev)
   10490          11 :         *pdfStdDev = dfStdDev;
   10491             : 
   10492          13 :     if (pnValidCount)
   10493          11 :         *pnValidCount = sData.nValidCount;
   10494             : 
   10495          13 :     SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
   10496          13 :                   sData.nValidCount, papszOptions);
   10497             : 
   10498          13 :     return true;
   10499             : }
   10500             : 
   10501             : /************************************************************************/
   10502             : /*                            SetStatistics()                           */
   10503             : /************************************************************************/
   10504             : //! @cond Doxygen_Suppress
   10505           5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
   10506             :                                 double /* dfMax */, double /* dfMean */,
   10507             :                                 double /* dfStdDev */,
   10508             :                                 GUInt64 /* nValidCount */,
   10509             :                                 CSLConstList /* papszOptions */)
   10510             : {
   10511           5 :     CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
   10512           5 :     return false;
   10513             : }
   10514             : 
   10515             : //! @endcond
   10516             : 
   10517             : /************************************************************************/
   10518             : /*                           ClearStatistics()                          */
   10519             : /************************************************************************/
   10520             : 
   10521             : /**
   10522             :  * \brief Clear statistics.
   10523             :  *
   10524             :  * @since GDAL 3.4
   10525             :  */
   10526           0 : void GDALMDArray::ClearStatistics()
   10527             : {
   10528           0 : }
   10529             : 
   10530             : /************************************************************************/
   10531             : /*                      GetCoordinateVariables()                        */
   10532             : /************************************************************************/
   10533             : 
   10534             : /**
   10535             :  * \brief Return coordinate variables.
   10536             :  *
   10537             :  * Coordinate variables are an alternate way of indexing an array that can
   10538             :  * be sometimes used. For example, an array collected through remote sensing
   10539             :  * might be indexed by (scanline, pixel). But there can be
   10540             :  * a longitude and latitude arrays alongside that are also both indexed by
   10541             :  * (scanline, pixel), and are referenced from operational arrays for
   10542             :  * reprojection purposes.
   10543             :  *
   10544             :  * For netCDF, this will return the arrays referenced by the "coordinates"
   10545             :  * attribute.
   10546             :  *
   10547             :  * This method is the same as the C function
   10548             :  * GDALMDArrayGetCoordinateVariables().
   10549             :  *
   10550             :  * @return a vector of arrays
   10551             :  *
   10552             :  * @since GDAL 3.4
   10553             :  */
   10554             : 
   10555             : std::vector<std::shared_ptr<GDALMDArray>>
   10556          13 : GDALMDArray::GetCoordinateVariables() const
   10557             : {
   10558          13 :     return {};
   10559             : }
   10560             : 
   10561             : /************************************************************************/
   10562             : /*                       ~GDALExtendedDataType()                        */
   10563             : /************************************************************************/
   10564             : 
   10565             : GDALExtendedDataType::~GDALExtendedDataType() = default;
   10566             : 
   10567             : /************************************************************************/
   10568             : /*                        GDALExtendedDataType()                        */
   10569             : /************************************************************************/
   10570             : 
   10571       14858 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
   10572       14858 :                                            GDALExtendedDataTypeSubType eSubType)
   10573             :     : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
   10574       14858 :       m_nMaxStringLength(nMaxStringLength)
   10575             : {
   10576       14858 : }
   10577             : 
   10578             : /************************************************************************/
   10579             : /*                        GDALExtendedDataType()                        */
   10580             : /************************************************************************/
   10581             : 
   10582       64947 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
   10583             :     : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
   10584       64947 :       m_nSize(GDALGetDataTypeSizeBytes(eType))
   10585             : {
   10586       64947 : }
   10587             : 
   10588             : /************************************************************************/
   10589             : /*                        GDALExtendedDataType()                        */
   10590             : /************************************************************************/
   10591             : 
   10592          63 : GDALExtendedDataType::GDALExtendedDataType(
   10593             :     const std::string &osName, GDALDataType eBaseType,
   10594          63 :     std::unique_ptr<GDALRasterAttributeTable> poRAT)
   10595             :     : m_osName(osName), m_eClass(GEDTC_NUMERIC), m_eNumericDT(eBaseType),
   10596          63 :       m_nSize(GDALGetDataTypeSizeBytes(eBaseType)), m_poRAT(std::move(poRAT))
   10597             : {
   10598          63 : }
   10599             : 
   10600             : /************************************************************************/
   10601             : /*                        GDALExtendedDataType()                        */
   10602             : /************************************************************************/
   10603             : 
   10604         915 : GDALExtendedDataType::GDALExtendedDataType(
   10605             :     const std::string &osName, size_t nTotalSize,
   10606         915 :     std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
   10607             :     : m_osName(osName), m_eClass(GEDTC_COMPOUND),
   10608         915 :       m_aoComponents(std::move(components)), m_nSize(nTotalSize)
   10609             : {
   10610         915 : }
   10611             : 
   10612             : /************************************************************************/
   10613             : /*                        GDALExtendedDataType()                        */
   10614             : /************************************************************************/
   10615             : 
   10616             : /** Move constructor. */
   10617             : GDALExtendedDataType::GDALExtendedDataType(GDALExtendedDataType &&) = default;
   10618             : 
   10619             : /************************************************************************/
   10620             : /*                        GDALExtendedDataType()                        */
   10621             : /************************************************************************/
   10622             : 
   10623             : /** Copy constructor. */
   10624       17102 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
   10625       34204 :     : m_osName(other.m_osName), m_eClass(other.m_eClass),
   10626       17102 :       m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
   10627       17102 :       m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength),
   10628       17102 :       m_poRAT(other.m_poRAT ? other.m_poRAT->Clone() : nullptr)
   10629             : {
   10630       17102 :     if (m_eClass == GEDTC_COMPOUND)
   10631             :     {
   10632         481 :         for (const auto &elt : other.m_aoComponents)
   10633             :         {
   10634         318 :             m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
   10635             :         }
   10636             :     }
   10637       17102 : }
   10638             : 
   10639             : /************************************************************************/
   10640             : /*                            operator= ()                              */
   10641             : /************************************************************************/
   10642             : 
   10643             : /** Copy assignment. */
   10644             : GDALExtendedDataType &
   10645        1088 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
   10646             : {
   10647        1088 :     if (this != &other)
   10648             :     {
   10649        1088 :         m_osName = other.m_osName;
   10650        1088 :         m_eClass = other.m_eClass;
   10651        1088 :         m_eSubType = other.m_eSubType;
   10652        1088 :         m_eNumericDT = other.m_eNumericDT;
   10653        1088 :         m_nSize = other.m_nSize;
   10654        1088 :         m_nMaxStringLength = other.m_nMaxStringLength;
   10655        1088 :         m_poRAT.reset(other.m_poRAT ? other.m_poRAT->Clone() : nullptr);
   10656        1088 :         m_aoComponents.clear();
   10657        1088 :         if (m_eClass == GEDTC_COMPOUND)
   10658             :         {
   10659           0 :             for (const auto &elt : other.m_aoComponents)
   10660             :             {
   10661           0 :                 m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
   10662             :             }
   10663             :         }
   10664             :     }
   10665        1088 :     return *this;
   10666             : }
   10667             : 
   10668             : /************************************************************************/
   10669             : /*                            operator= ()                              */
   10670             : /************************************************************************/
   10671             : 
   10672             : /** Move assignment. */
   10673             : GDALExtendedDataType &
   10674             : GDALExtendedDataType::operator=(GDALExtendedDataType &&other) = default;
   10675             : 
   10676             : /************************************************************************/
   10677             : /*                           Create()                                   */
   10678             : /************************************************************************/
   10679             : 
   10680             : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
   10681             :  *
   10682             :  * This is the same as the C function GDALExtendedDataTypeCreate()
   10683             :  *
   10684             :  * @param eType Numeric data type. Must be different from GDT_Unknown and
   10685             :  * GDT_TypeCount
   10686             :  */
   10687       64940 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
   10688             : {
   10689       64940 :     return GDALExtendedDataType(eType);
   10690             : }
   10691             : 
   10692             : /************************************************************************/
   10693             : /*                           Create()                                   */
   10694             : /************************************************************************/
   10695             : 
   10696             : /** Return a new GDALExtendedDataType from a raster attribute table.
   10697             :  *
   10698             :  * @param osName Type name
   10699             :  * @param eBaseType Base integer data type.
   10700             :  * @param poRAT Raster attribute table. Must not be NULL.
   10701             :  * @since 3.12
   10702             :  */
   10703             : GDALExtendedDataType
   10704          63 : GDALExtendedDataType::Create(const std::string &osName, GDALDataType eBaseType,
   10705             :                              std::unique_ptr<GDALRasterAttributeTable> poRAT)
   10706             : {
   10707          63 :     return GDALExtendedDataType(osName, eBaseType, std::move(poRAT));
   10708             : }
   10709             : 
   10710             : /************************************************************************/
   10711             : /*                           Create()                                   */
   10712             : /************************************************************************/
   10713             : 
   10714             : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
   10715             :  *
   10716             :  * This is the same as the C function GDALExtendedDataTypeCreateCompound()
   10717             :  *
   10718             :  * @param osName Type name.
   10719             :  * @param nTotalSize Total size of the type in bytes.
   10720             :  *                   Should be large enough to store all components.
   10721             :  * @param components Components of the compound type.
   10722             :  */
   10723         922 : GDALExtendedDataType GDALExtendedDataType::Create(
   10724             :     const std::string &osName, size_t nTotalSize,
   10725             :     std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
   10726             : {
   10727         922 :     size_t nLastOffset = 0;
   10728             :     // Some arbitrary threshold to avoid potential integer overflows
   10729         922 :     if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
   10730             :     {
   10731           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10732           2 :         return GDALExtendedDataType(GDT_Unknown);
   10733             :     }
   10734        4306 :     for (const auto &comp : components)
   10735             :     {
   10736             :         // Check alignment too ?
   10737        3387 :         if (comp->GetOffset() < nLastOffset)
   10738             :         {
   10739           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10740           1 :             return GDALExtendedDataType(GDT_Unknown);
   10741             :         }
   10742        3386 :         nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
   10743             :     }
   10744         919 :     if (nTotalSize < nLastOffset)
   10745             :     {
   10746           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10747           1 :         return GDALExtendedDataType(GDT_Unknown);
   10748             :     }
   10749         918 :     if (nTotalSize == 0 || components.empty())
   10750             :     {
   10751           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
   10752           3 :         return GDALExtendedDataType(GDT_Unknown);
   10753             :     }
   10754         915 :     return GDALExtendedDataType(osName, nTotalSize, std::move(components));
   10755             : }
   10756             : 
   10757             : /************************************************************************/
   10758             : /*                           Create()                                   */
   10759             : /************************************************************************/
   10760             : 
   10761             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   10762             :  *
   10763             :  * This is the same as the C function GDALExtendedDataTypeCreateString().
   10764             :  *
   10765             :  * @param nMaxStringLength maximum length of a string in bytes. 0 if
   10766             :  * unknown/unlimited
   10767             :  * @param eSubType Subtype.
   10768             :  */
   10769             : GDALExtendedDataType
   10770       14858 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
   10771             :                                    GDALExtendedDataTypeSubType eSubType)
   10772             : {
   10773       14858 :     return GDALExtendedDataType(nMaxStringLength, eSubType);
   10774             : }
   10775             : 
   10776             : /************************************************************************/
   10777             : /*                           operator==()                               */
   10778             : /************************************************************************/
   10779             : 
   10780             : /** Equality operator.
   10781             :  *
   10782             :  * This is the same as the C function GDALExtendedDataTypeEquals().
   10783             :  */
   10784        3378 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
   10785             : {
   10786        3351 :     if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
   10787        6729 :         m_nSize != other.m_nSize || m_osName != other.m_osName)
   10788             :     {
   10789         324 :         return false;
   10790             :     }
   10791        3054 :     if (m_eClass == GEDTC_NUMERIC)
   10792             :     {
   10793         940 :         return m_eNumericDT == other.m_eNumericDT;
   10794             :     }
   10795        2114 :     if (m_eClass == GEDTC_STRING)
   10796             :     {
   10797        1867 :         return true;
   10798             :     }
   10799         247 :     CPLAssert(m_eClass == GEDTC_COMPOUND);
   10800         247 :     if (m_aoComponents.size() != other.m_aoComponents.size())
   10801             :     {
   10802           2 :         return false;
   10803             :     }
   10804        1232 :     for (size_t i = 0; i < m_aoComponents.size(); i++)
   10805             :     {
   10806         987 :         if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
   10807             :         {
   10808           0 :             return false;
   10809             :         }
   10810             :     }
   10811         245 :     return true;
   10812             : }
   10813             : 
   10814             : /************************************************************************/
   10815             : /*                        CanConvertTo()                                */
   10816             : /************************************************************************/
   10817             : 
   10818             : /** Return whether this data type can be converted to the other one.
   10819             :  *
   10820             :  * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
   10821             :  *
   10822             :  * @param other Target data type for the conversion being considered.
   10823             :  */
   10824       10938 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
   10825             : {
   10826       10938 :     if (m_eClass == GEDTC_NUMERIC)
   10827             :     {
   10828        7340 :         if (m_eNumericDT == GDT_Unknown)
   10829           0 :             return false;
   10830        7340 :         if (other.m_eClass == GEDTC_NUMERIC &&
   10831        7129 :             other.m_eNumericDT == GDT_Unknown)
   10832           0 :             return false;
   10833        7551 :         return other.m_eClass == GEDTC_NUMERIC ||
   10834        7551 :                other.m_eClass == GEDTC_STRING;
   10835             :     }
   10836        3598 :     if (m_eClass == GEDTC_STRING)
   10837             :     {
   10838        3377 :         return other.m_eClass == m_eClass;
   10839             :     }
   10840         221 :     CPLAssert(m_eClass == GEDTC_COMPOUND);
   10841         221 :     if (other.m_eClass != GEDTC_COMPOUND)
   10842           0 :         return false;
   10843             :     std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
   10844         442 :         srcComponents;
   10845        1052 :     for (const auto &srcComp : m_aoComponents)
   10846             :     {
   10847         831 :         srcComponents[srcComp->GetName()] = &srcComp;
   10848             :     }
   10849         583 :     for (const auto &dstComp : other.m_aoComponents)
   10850             :     {
   10851         363 :         auto oIter = srcComponents.find(dstComp->GetName());
   10852         363 :         if (oIter == srcComponents.end())
   10853           1 :             return false;
   10854         362 :         if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
   10855           0 :             return false;
   10856             :     }
   10857         220 :     return true;
   10858             : }
   10859             : 
   10860             : /************************************************************************/
   10861             : /*                     NeedsFreeDynamicMemory()                         */
   10862             : /************************************************************************/
   10863             : 
   10864             : /** Return whether the data type holds dynamically allocated memory, that
   10865             :  * needs to be freed with FreeDynamicMemory().
   10866             :  *
   10867             :  */
   10868        4035 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
   10869             : {
   10870        4035 :     switch (m_eClass)
   10871             :     {
   10872        1079 :         case GEDTC_STRING:
   10873        1079 :             return true;
   10874             : 
   10875        2833 :         case GEDTC_NUMERIC:
   10876        2833 :             return false;
   10877             : 
   10878         123 :         case GEDTC_COMPOUND:
   10879             :         {
   10880         244 :             for (const auto &comp : m_aoComponents)
   10881             :             {
   10882         222 :                 if (comp->GetType().NeedsFreeDynamicMemory())
   10883         101 :                     return true;
   10884             :             }
   10885             :         }
   10886             :     }
   10887          22 :     return false;
   10888             : }
   10889             : 
   10890             : /************************************************************************/
   10891             : /*                        FreeDynamicMemory()                           */
   10892             : /************************************************************************/
   10893             : 
   10894             : /** Release the dynamic memory (strings typically) from a raw value.
   10895             :  *
   10896             :  * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
   10897             :  *
   10898             :  * @param pBuffer Raw buffer of a single element of an attribute or array value.
   10899             :  */
   10900        4194 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
   10901             : {
   10902        4194 :     switch (m_eClass)
   10903             :     {
   10904        3017 :         case GEDTC_STRING:
   10905             :         {
   10906             :             char *pszStr;
   10907        3017 :             memcpy(&pszStr, pBuffer, sizeof(char *));
   10908        3017 :             if (pszStr)
   10909             :             {
   10910        2419 :                 VSIFree(pszStr);
   10911             :             }
   10912        3017 :             break;
   10913             :         }
   10914             : 
   10915         988 :         case GEDTC_NUMERIC:
   10916             :         {
   10917         988 :             break;
   10918             :         }
   10919             : 
   10920         189 :         case GEDTC_COMPOUND:
   10921             :         {
   10922         189 :             GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
   10923         999 :             for (const auto &comp : m_aoComponents)
   10924             :             {
   10925        1620 :                 comp->GetType().FreeDynamicMemory(pabyBuffer +
   10926         810 :                                                   comp->GetOffset());
   10927             :             }
   10928         189 :             break;
   10929             :         }
   10930             :     }
   10931        4194 : }
   10932             : 
   10933             : /************************************************************************/
   10934             : /*                      ~GDALEDTComponent()                             */
   10935             : /************************************************************************/
   10936             : 
   10937             : GDALEDTComponent::~GDALEDTComponent() = default;
   10938             : 
   10939             : /************************************************************************/
   10940             : /*                      GDALEDTComponent()                              */
   10941             : /************************************************************************/
   10942             : 
   10943             : /** constructor of a GDALEDTComponent
   10944             :  *
   10945             :  * This is the same as the C function GDALEDTComponendCreate()
   10946             :  *
   10947             :  * @param name Component name
   10948             :  * @param offset Offset in byte of the component in the compound data type.
   10949             :  *               In case of nesting of compound data type, this should be
   10950             :  *               the offset to the immediate belonging data type, not to the
   10951             :  *               higher level one.
   10952             :  * @param type   Component data type.
   10953             :  */
   10954        3378 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
   10955        3378 :                                    const GDALExtendedDataType &type)
   10956        3378 :     : m_osName(name), m_nOffset(offset), m_oType(type)
   10957             : {
   10958        3378 : }
   10959             : 
   10960             : /************************************************************************/
   10961             : /*                      GDALEDTComponent()                              */
   10962             : /************************************************************************/
   10963             : 
   10964             : /** Copy constructor. */
   10965             : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
   10966             : 
   10967             : /************************************************************************/
   10968             : /*                           operator==()                               */
   10969             : /************************************************************************/
   10970             : 
   10971             : /** Equality operator.
   10972             :  */
   10973         987 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
   10974             : {
   10975        1974 :     return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
   10976        1974 :            m_oType == other.m_oType;
   10977             : }
   10978             : 
   10979             : /************************************************************************/
   10980             : /*                        ~GDALDimension()                              */
   10981             : /************************************************************************/
   10982             : 
   10983             : GDALDimension::~GDALDimension() = default;
   10984             : 
   10985             : /************************************************************************/
   10986             : /*                         GDALDimension()                              */
   10987             : /************************************************************************/
   10988             : 
   10989             : //! @cond Doxygen_Suppress
   10990             : /** Constructor.
   10991             :  *
   10992             :  * @param osParentName Parent name
   10993             :  * @param osName name
   10994             :  * @param osType type. See GetType().
   10995             :  * @param osDirection direction. See GetDirection().
   10996             :  * @param nSize size.
   10997             :  */
   10998        9474 : GDALDimension::GDALDimension(const std::string &osParentName,
   10999             :                              const std::string &osName,
   11000             :                              const std::string &osType,
   11001        9474 :                              const std::string &osDirection, GUInt64 nSize)
   11002             :     : m_osName(osName),
   11003             :       m_osFullName(
   11004        9474 :           !osParentName.empty()
   11005       14051 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
   11006             :               : osName),
   11007       32999 :       m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
   11008             : {
   11009        9474 : }
   11010             : 
   11011             : //! @endcond
   11012             : 
   11013             : /************************************************************************/
   11014             : /*                         GetIndexingVariable()                        */
   11015             : /************************************************************************/
   11016             : 
   11017             : /** Return the variable that is used to index the dimension (if there is one).
   11018             :  *
   11019             :  * This is the array, typically one-dimensional, describing the values taken
   11020             :  * by the dimension.
   11021             :  */
   11022          43 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
   11023             : {
   11024          43 :     return nullptr;
   11025             : }
   11026             : 
   11027             : /************************************************************************/
   11028             : /*                         SetIndexingVariable()                        */
   11029             : /************************************************************************/
   11030             : 
   11031             : /** Set the variable that is used to index the dimension.
   11032             :  *
   11033             :  * This is the array, typically one-dimensional, describing the values taken
   11034             :  * by the dimension.
   11035             :  *
   11036             :  * Optionally implemented by drivers.
   11037             :  *
   11038             :  * Drivers known to implement it: MEM.
   11039             :  *
   11040             :  * @param poArray Variable to use to index the dimension.
   11041             :  * @return true in case of success.
   11042             :  */
   11043          11 : bool GDALDimension::SetIndexingVariable(
   11044             :     CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
   11045             : {
   11046          11 :     CPLError(CE_Failure, CPLE_NotSupported,
   11047             :              "SetIndexingVariable() not implemented");
   11048          11 :     return false;
   11049             : }
   11050             : 
   11051             : /************************************************************************/
   11052             : /*                            Rename()                                  */
   11053             : /************************************************************************/
   11054             : 
   11055             : /** Rename the dimension.
   11056             :  *
   11057             :  * This is not implemented by all drivers.
   11058             :  *
   11059             :  * Drivers known to implement it: MEM, netCDF, ZARR.
   11060             :  *
   11061             :  * This is the same as the C function GDALDimensionRename().
   11062             :  *
   11063             :  * @param osNewName New name.
   11064             :  *
   11065             :  * @return true in case of success
   11066             :  * @since GDAL 3.8
   11067             :  */
   11068           0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
   11069             : {
   11070           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
   11071           0 :     return false;
   11072             : }
   11073             : 
   11074             : /************************************************************************/
   11075             : /*                         BaseRename()                                 */
   11076             : /************************************************************************/
   11077             : 
   11078             : //! @cond Doxygen_Suppress
   11079           8 : void GDALDimension::BaseRename(const std::string &osNewName)
   11080             : {
   11081           8 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
   11082           8 :     m_osFullName += osNewName;
   11083           8 :     m_osName = osNewName;
   11084           8 : }
   11085             : 
   11086             : //! @endcond
   11087             : 
   11088             : //! @cond Doxygen_Suppress
   11089             : /************************************************************************/
   11090             : /*                          ParentRenamed()                             */
   11091             : /************************************************************************/
   11092             : 
   11093           8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
   11094             : {
   11095           8 :     m_osFullName = osNewParentFullName;
   11096           8 :     m_osFullName += "/";
   11097           8 :     m_osFullName += m_osName;
   11098           8 : }
   11099             : 
   11100             : //! @endcond
   11101             : 
   11102             : //! @cond Doxygen_Suppress
   11103             : /************************************************************************/
   11104             : /*                          ParentDeleted()                             */
   11105             : /************************************************************************/
   11106             : 
   11107           4 : void GDALDimension::ParentDeleted()
   11108             : {
   11109           4 : }
   11110             : 
   11111             : //! @endcond
   11112             : 
   11113             : /************************************************************************/
   11114             : /************************************************************************/
   11115             : /************************************************************************/
   11116             : /*                              C API                                   */
   11117             : /************************************************************************/
   11118             : /************************************************************************/
   11119             : /************************************************************************/
   11120             : 
   11121             : /************************************************************************/
   11122             : /*                      GDALExtendedDataTypeCreate()                    */
   11123             : /************************************************************************/
   11124             : 
   11125             : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
   11126             :  *
   11127             :  * This is the same as the C++ method GDALExtendedDataType::Create()
   11128             :  *
   11129             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   11130             :  *
   11131             :  * @param eType Numeric data type. Must be different from GDT_Unknown and
   11132             :  * GDT_TypeCount
   11133             :  *
   11134             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   11135             :  */
   11136        2148 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
   11137             : {
   11138        2148 :     if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
   11139             :     {
   11140           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
   11141             :                  "Illegal GDT_Unknown/GDT_TypeCount argument");
   11142           0 :         return nullptr;
   11143             :     }
   11144             :     return new GDALExtendedDataTypeHS(
   11145        2148 :         new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
   11146             : }
   11147             : 
   11148             : /************************************************************************/
   11149             : /*                    GDALExtendedDataTypeCreateString()                */
   11150             : /************************************************************************/
   11151             : 
   11152             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   11153             :  *
   11154             :  * This is the same as the C++ method GDALExtendedDataType::CreateString()
   11155             :  *
   11156             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   11157             :  *
   11158             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   11159             :  */
   11160           0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
   11161             : {
   11162           0 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   11163           0 :         GDALExtendedDataType::CreateString(nMaxStringLength)));
   11164             : }
   11165             : 
   11166             : /************************************************************************/
   11167             : /*                   GDALExtendedDataTypeCreateStringEx()               */
   11168             : /************************************************************************/
   11169             : 
   11170             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   11171             :  *
   11172             :  * This is the same as the C++ method GDALExtendedDataType::CreateString()
   11173             :  *
   11174             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   11175             :  *
   11176             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   11177             :  * @since GDAL 3.4
   11178             :  */
   11179             : GDALExtendedDataTypeH
   11180         222 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
   11181             :                                    GDALExtendedDataTypeSubType eSubType)
   11182             : {
   11183         222 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   11184         222 :         GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
   11185             : }
   11186             : 
   11187             : /************************************************************************/
   11188             : /*                   GDALExtendedDataTypeCreateCompound()               */
   11189             : /************************************************************************/
   11190             : 
   11191             : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
   11192             :  *
   11193             :  * This is the same as the C++ method GDALExtendedDataType::Create(const
   11194             :  * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
   11195             :  *
   11196             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   11197             :  *
   11198             :  * @param pszName Type name.
   11199             :  * @param nTotalSize Total size of the type in bytes.
   11200             :  *                   Should be large enough to store all components.
   11201             :  * @param nComponents Number of components in comps array.
   11202             :  * @param comps Components.
   11203             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   11204             :  */
   11205             : GDALExtendedDataTypeH
   11206          22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
   11207             :                                    size_t nComponents,
   11208             :                                    const GDALEDTComponentH *comps)
   11209             : {
   11210          44 :     std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
   11211          54 :     for (size_t i = 0; i < nComponents; i++)
   11212             :     {
   11213             :         compsCpp.emplace_back(
   11214          32 :             std::make_unique<GDALEDTComponent>(*(comps[i]->m_poImpl.get())));
   11215             :     }
   11216             :     auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
   11217          66 :                                            std::move(compsCpp));
   11218          22 :     if (dt.GetClass() != GEDTC_COMPOUND)
   11219           6 :         return nullptr;
   11220          16 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(std::move(dt)));
   11221             : }
   11222             : 
   11223             : /************************************************************************/
   11224             : /*                     GDALExtendedDataTypeRelease()                    */
   11225             : /************************************************************************/
   11226             : 
   11227             : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
   11228             :  *
   11229             :  * Note: when applied on a object coming from a driver, this does not
   11230             :  * destroy the object in the file, database, etc...
   11231             :  */
   11232        7081 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
   11233             : {
   11234        7081 :     delete hEDT;
   11235        7081 : }
   11236             : 
   11237             : /************************************************************************/
   11238             : /*                     GDALExtendedDataTypeGetName()                    */
   11239             : /************************************************************************/
   11240             : 
   11241             : /** Return type name.
   11242             :  *
   11243             :  * This is the same as the C++ method GDALExtendedDataType::GetName()
   11244             :  */
   11245           8 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
   11246             : {
   11247           8 :     VALIDATE_POINTER1(hEDT, __func__, "");
   11248           8 :     return hEDT->m_poImpl->GetName().c_str();
   11249             : }
   11250             : 
   11251             : /************************************************************************/
   11252             : /*                     GDALExtendedDataTypeGetClass()                    */
   11253             : /************************************************************************/
   11254             : 
   11255             : /** Return type class.
   11256             :  *
   11257             :  * This is the same as the C++ method GDALExtendedDataType::GetClass()
   11258             :  */
   11259             : GDALExtendedDataTypeClass
   11260        9613 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
   11261             : {
   11262        9613 :     VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
   11263        9613 :     return hEDT->m_poImpl->GetClass();
   11264             : }
   11265             : 
   11266             : /************************************************************************/
   11267             : /*               GDALExtendedDataTypeGetNumericDataType()               */
   11268             : /************************************************************************/
   11269             : 
   11270             : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
   11271             :  *
   11272             :  * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
   11273             :  */
   11274        2142 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
   11275             : {
   11276        2142 :     VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
   11277        2142 :     return hEDT->m_poImpl->GetNumericDataType();
   11278             : }
   11279             : 
   11280             : /************************************************************************/
   11281             : /*                   GDALExtendedDataTypeGetSize()                      */
   11282             : /************************************************************************/
   11283             : 
   11284             : /** Return data type size in bytes.
   11285             :  *
   11286             :  * This is the same as the C++ method GDALExtendedDataType::GetSize()
   11287             :  */
   11288        2668 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
   11289             : {
   11290        2668 :     VALIDATE_POINTER1(hEDT, __func__, 0);
   11291        2668 :     return hEDT->m_poImpl->GetSize();
   11292             : }
   11293             : 
   11294             : /************************************************************************/
   11295             : /*              GDALExtendedDataTypeGetMaxStringLength()                */
   11296             : /************************************************************************/
   11297             : 
   11298             : /** Return the maximum length of a string in bytes.
   11299             :  *
   11300             :  * 0 indicates unknown/unlimited string.
   11301             :  *
   11302             :  * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
   11303             :  */
   11304           3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
   11305             : {
   11306           3 :     VALIDATE_POINTER1(hEDT, __func__, 0);
   11307           3 :     return hEDT->m_poImpl->GetMaxStringLength();
   11308             : }
   11309             : 
   11310             : /************************************************************************/
   11311             : /*                    GDALExtendedDataTypeCanConvertTo()                */
   11312             : /************************************************************************/
   11313             : 
   11314             : /** Return whether this data type can be converted to the other one.
   11315             :  *
   11316             :  * This is the same as the C function GDALExtendedDataType::CanConvertTo()
   11317             :  *
   11318             :  * @param hSourceEDT Source data type for the conversion being considered.
   11319             :  * @param hTargetEDT Target data type for the conversion being considered.
   11320             :  * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
   11321             :  */
   11322           7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
   11323             :                                      GDALExtendedDataTypeH hTargetEDT)
   11324             : {
   11325           7 :     VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
   11326           7 :     VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
   11327           7 :     return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
   11328             : }
   11329             : 
   11330             : /************************************************************************/
   11331             : /*                        GDALExtendedDataTypeEquals()                  */
   11332             : /************************************************************************/
   11333             : 
   11334             : /** Return whether this data type is equal to another one.
   11335             :  *
   11336             :  * This is the same as the C++ method GDALExtendedDataType::operator==()
   11337             :  *
   11338             :  * @param hFirstEDT First data type.
   11339             :  * @param hSecondEDT Second data type.
   11340             :  * @return TRUE if they are equal. FALSE otherwise.
   11341             :  */
   11342         100 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
   11343             :                                GDALExtendedDataTypeH hSecondEDT)
   11344             : {
   11345         100 :     VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
   11346         100 :     VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
   11347         100 :     return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
   11348             : }
   11349             : 
   11350             : /************************************************************************/
   11351             : /*                    GDALExtendedDataTypeGetSubType()                  */
   11352             : /************************************************************************/
   11353             : 
   11354             : /** Return the subtype of a type.
   11355             :  *
   11356             :  * This is the same as the C++ method GDALExtendedDataType::GetSubType()
   11357             :  *
   11358             :  * @param hEDT Data type.
   11359             :  * @return subtype.
   11360             :  * @since 3.4
   11361             :  */
   11362             : GDALExtendedDataTypeSubType
   11363         105 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
   11364             : {
   11365         105 :     VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
   11366         105 :     return hEDT->m_poImpl->GetSubType();
   11367             : }
   11368             : 
   11369             : /************************************************************************/
   11370             : /*                      GDALExtendedDataTypeGetRAT()                    */
   11371             : /************************************************************************/
   11372             : 
   11373             : /** Return associated raster attribute table, when there is one.
   11374             :  *
   11375             :  * * For the netCDF driver, the RAT will capture enumerated types, with
   11376             :  * a "value" column with an integer value and a "name" column with the
   11377             :  * associated name.
   11378             :  * This is the same as the C++ method GDALExtendedDataType::GetRAT()
   11379             :  *
   11380             :  * @param hEDT Data type.
   11381             :  * @return raster attribute (owned by GDALExtendedDataTypeH), or NULL
   11382             :  * @since 3.12
   11383             :  */
   11384           1 : GDALRasterAttributeTableH GDALExtendedDataTypeGetRAT(GDALExtendedDataTypeH hEDT)
   11385             : {
   11386           1 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11387           1 :     return GDALRasterAttributeTable::ToHandle(
   11388           2 :         const_cast<GDALRasterAttributeTable *>(hEDT->m_poImpl->GetRAT()));
   11389             : }
   11390             : 
   11391             : /************************************************************************/
   11392             : /*                     GDALExtendedDataTypeGetComponents()              */
   11393             : /************************************************************************/
   11394             : 
   11395             : /** Return the components of the data type (only valid when GetClass() ==
   11396             :  * GEDTC_COMPOUND)
   11397             :  *
   11398             :  * The returned array and its content must be freed with
   11399             :  * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
   11400             :  * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
   11401             :  * individual array members).
   11402             :  *
   11403             :  * This is the same as the C++ method GDALExtendedDataType::GetComponents()
   11404             :  *
   11405             :  * @param hEDT Data type
   11406             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11407             :  * @return an array of *pnCount components.
   11408             :  */
   11409          44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
   11410             :                                                      size_t *pnCount)
   11411             : {
   11412          44 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11413          44 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11414          44 :     const auto &components = hEDT->m_poImpl->GetComponents();
   11415             :     auto ret = static_cast<GDALEDTComponentH *>(
   11416          44 :         CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
   11417         131 :     for (size_t i = 0; i < components.size(); i++)
   11418             :     {
   11419          87 :         ret[i] = new GDALEDTComponentHS(*components[i].get());
   11420             :     }
   11421          44 :     *pnCount = components.size();
   11422          44 :     return ret;
   11423             : }
   11424             : 
   11425             : /************************************************************************/
   11426             : /*                     GDALExtendedDataTypeFreeComponents()             */
   11427             : /************************************************************************/
   11428             : 
   11429             : /** Free the return of GDALExtendedDataTypeGetComponents().
   11430             :  *
   11431             :  * @param components return value of GDALExtendedDataTypeGetComponents()
   11432             :  * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
   11433             :  */
   11434          44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
   11435             :                                         size_t nCount)
   11436             : {
   11437         131 :     for (size_t i = 0; i < nCount; i++)
   11438             :     {
   11439          87 :         delete components[i];
   11440             :     }
   11441          44 :     CPLFree(components);
   11442          44 : }
   11443             : 
   11444             : /************************************************************************/
   11445             : /*                         GDALEDTComponentCreate()                     */
   11446             : /************************************************************************/
   11447             : 
   11448             : /** Create a new GDALEDTComponent.
   11449             :  *
   11450             :  * The returned value must be freed with GDALEDTComponentRelease().
   11451             :  *
   11452             :  * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
   11453             :  */
   11454          20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
   11455             :                                          GDALExtendedDataTypeH hType)
   11456             : {
   11457          20 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11458          20 :     VALIDATE_POINTER1(hType, __func__, nullptr);
   11459             :     return new GDALEDTComponentHS(
   11460          20 :         GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
   11461             : }
   11462             : 
   11463             : /************************************************************************/
   11464             : /*                         GDALEDTComponentRelease()                    */
   11465             : /************************************************************************/
   11466             : 
   11467             : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
   11468             :  *
   11469             :  * Note: when applied on a object coming from a driver, this does not
   11470             :  * destroy the object in the file, database, etc...
   11471             :  */
   11472          61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
   11473             : {
   11474          61 :     delete hComp;
   11475          61 : }
   11476             : 
   11477             : /************************************************************************/
   11478             : /*                         GDALEDTComponentGetName()                    */
   11479             : /************************************************************************/
   11480             : 
   11481             : /** Return the name.
   11482             :  *
   11483             :  * The returned pointer is valid until hComp is released.
   11484             :  *
   11485             :  * This is the same as the C++ method GDALEDTComponent::GetName().
   11486             :  */
   11487          33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
   11488             : {
   11489          33 :     VALIDATE_POINTER1(hComp, __func__, nullptr);
   11490          33 :     return hComp->m_poImpl->GetName().c_str();
   11491             : }
   11492             : 
   11493             : /************************************************************************/
   11494             : /*                       GDALEDTComponentGetOffset()                    */
   11495             : /************************************************************************/
   11496             : 
   11497             : /** Return the offset (in bytes) of the component in the compound data type.
   11498             :  *
   11499             :  * This is the same as the C++ method GDALEDTComponent::GetOffset().
   11500             :  */
   11501          31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
   11502             : {
   11503          31 :     VALIDATE_POINTER1(hComp, __func__, 0);
   11504          31 :     return hComp->m_poImpl->GetOffset();
   11505             : }
   11506             : 
   11507             : /************************************************************************/
   11508             : /*                       GDALEDTComponentGetType()                      */
   11509             : /************************************************************************/
   11510             : 
   11511             : /** Return the data type of the component.
   11512             :  *
   11513             :  * This is the same as the C++ method GDALEDTComponent::GetType().
   11514             :  */
   11515          93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
   11516             : {
   11517          93 :     VALIDATE_POINTER1(hComp, __func__, nullptr);
   11518             :     return new GDALExtendedDataTypeHS(
   11519          93 :         new GDALExtendedDataType(hComp->m_poImpl->GetType()));
   11520             : }
   11521             : 
   11522             : /************************************************************************/
   11523             : /*                           GDALGroupRelease()                         */
   11524             : /************************************************************************/
   11525             : 
   11526             : /** Release the GDAL in-memory object associated with a GDALGroupH.
   11527             :  *
   11528             :  * Note: when applied on a object coming from a driver, this does not
   11529             :  * destroy the object in the file, database, etc...
   11530             :  */
   11531        1535 : void GDALGroupRelease(GDALGroupH hGroup)
   11532             : {
   11533        1535 :     delete hGroup;
   11534        1535 : }
   11535             : 
   11536             : /************************************************************************/
   11537             : /*                           GDALGroupGetName()                         */
   11538             : /************************************************************************/
   11539             : 
   11540             : /** Return the name of the group.
   11541             :  *
   11542             :  * The returned pointer is valid until hGroup is released.
   11543             :  *
   11544             :  * This is the same as the C++ method GDALGroup::GetName().
   11545             :  */
   11546          95 : const char *GDALGroupGetName(GDALGroupH hGroup)
   11547             : {
   11548          95 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11549          95 :     return hGroup->m_poImpl->GetName().c_str();
   11550             : }
   11551             : 
   11552             : /************************************************************************/
   11553             : /*                         GDALGroupGetFullName()                       */
   11554             : /************************************************************************/
   11555             : 
   11556             : /** Return the full name of the group.
   11557             :  *
   11558             :  * The returned pointer is valid until hGroup is released.
   11559             :  *
   11560             :  * This is the same as the C++ method GDALGroup::GetFullName().
   11561             :  */
   11562          47 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
   11563             : {
   11564          47 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11565          47 :     return hGroup->m_poImpl->GetFullName().c_str();
   11566             : }
   11567             : 
   11568             : /************************************************************************/
   11569             : /*                          GDALGroupGetMDArrayNames()                  */
   11570             : /************************************************************************/
   11571             : 
   11572             : /** Return the list of multidimensional array names contained in this group.
   11573             :  *
   11574             :  * This is the same as the C++ method GDALGroup::GetGroupNames().
   11575             :  *
   11576             :  * @return the array names, to be freed with CSLDestroy()
   11577             :  */
   11578         337 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
   11579             : {
   11580         337 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11581         674 :     auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
   11582         674 :     CPLStringList res;
   11583         851 :     for (const auto &name : names)
   11584             :     {
   11585         514 :         res.AddString(name.c_str());
   11586             :     }
   11587         337 :     return res.StealList();
   11588             : }
   11589             : 
   11590             : /************************************************************************/
   11591             : /*                  GDALGroupGetMDArrayFullNamesRecursive()             */
   11592             : /************************************************************************/
   11593             : 
   11594             : /** Return the list of multidimensional array full names contained in this
   11595             :  * group and its subgroups.
   11596             :  *
   11597             :  * This is the same as the C++ method GDALGroup::GetMDArrayFullNamesRecursive().
   11598             :  *
   11599             :  * @return the array names, to be freed with CSLDestroy()
   11600             :  *
   11601             :  * @since 3.11
   11602             :  */
   11603           1 : char **GDALGroupGetMDArrayFullNamesRecursive(GDALGroupH hGroup,
   11604             :                                              CSLConstList papszGroupOptions,
   11605             :                                              CSLConstList papszArrayOptions)
   11606             : {
   11607           1 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11608           1 :     auto names = hGroup->m_poImpl->GetMDArrayFullNamesRecursive(
   11609           2 :         papszGroupOptions, papszArrayOptions);
   11610           2 :     CPLStringList res;
   11611           5 :     for (const auto &name : names)
   11612             :     {
   11613           4 :         res.AddString(name.c_str());
   11614             :     }
   11615           1 :     return res.StealList();
   11616             : }
   11617             : 
   11618             : /************************************************************************/
   11619             : /*                          GDALGroupOpenMDArray()                      */
   11620             : /************************************************************************/
   11621             : 
   11622             : /** Open and return a multidimensional array.
   11623             :  *
   11624             :  * This is the same as the C++ method GDALGroup::OpenMDArray().
   11625             :  *
   11626             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11627             :  */
   11628         835 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
   11629             :                                   CSLConstList papszOptions)
   11630             : {
   11631         835 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11632         835 :     VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
   11633        2505 :     auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
   11634        2505 :                                                papszOptions);
   11635         835 :     if (!array)
   11636          30 :         return nullptr;
   11637         805 :     return new GDALMDArrayHS(array);
   11638             : }
   11639             : 
   11640             : /************************************************************************/
   11641             : /*                  GDALGroupOpenMDArrayFromFullname()                  */
   11642             : /************************************************************************/
   11643             : 
   11644             : /** Open and return a multidimensional array from its fully qualified name.
   11645             :  *
   11646             :  * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
   11647             :  *
   11648             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11649             :  *
   11650             :  * @since GDAL 3.2
   11651             :  */
   11652          18 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
   11653             :                                               const char *pszFullname,
   11654             :                                               CSLConstList papszOptions)
   11655             : {
   11656          18 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11657          18 :     VALIDATE_POINTER1(pszFullname, __func__, nullptr);
   11658          18 :     auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
   11659          54 :         std::string(pszFullname), papszOptions);
   11660          18 :     if (!array)
   11661           2 :         return nullptr;
   11662          16 :     return new GDALMDArrayHS(array);
   11663             : }
   11664             : 
   11665             : /************************************************************************/
   11666             : /*                      GDALGroupResolveMDArray()                       */
   11667             : /************************************************************************/
   11668             : 
   11669             : /** Locate an array in a group and its subgroups by name.
   11670             :  *
   11671             :  * See GDALGroup::ResolveMDArray() for description of the behavior.
   11672             :  * @since GDAL 3.2
   11673             :  */
   11674          19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
   11675             :                                      const char *pszStartingPoint,
   11676             :                                      CSLConstList papszOptions)
   11677             : {
   11678          19 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11679          19 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11680          19 :     VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
   11681          19 :     auto array = hGroup->m_poImpl->ResolveMDArray(
   11682          57 :         std::string(pszName), std::string(pszStartingPoint), papszOptions);
   11683          19 :     if (!array)
   11684           2 :         return nullptr;
   11685          17 :     return new GDALMDArrayHS(array);
   11686             : }
   11687             : 
   11688             : /************************************************************************/
   11689             : /*                        GDALGroupGetGroupNames()                      */
   11690             : /************************************************************************/
   11691             : 
   11692             : /** Return the list of sub-groups contained in this group.
   11693             :  *
   11694             :  * This is the same as the C++ method GDALGroup::GetGroupNames().
   11695             :  *
   11696             :  * @return the group names, to be freed with CSLDestroy()
   11697             :  */
   11698          98 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
   11699             : {
   11700          98 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11701         196 :     auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
   11702         196 :     CPLStringList res;
   11703         221 :     for (const auto &name : names)
   11704             :     {
   11705         123 :         res.AddString(name.c_str());
   11706             :     }
   11707          98 :     return res.StealList();
   11708             : }
   11709             : 
   11710             : /************************************************************************/
   11711             : /*                           GDALGroupOpenGroup()                       */
   11712             : /************************************************************************/
   11713             : 
   11714             : /** Open and return a sub-group.
   11715             :  *
   11716             :  * This is the same as the C++ method GDALGroup::OpenGroup().
   11717             :  *
   11718             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11719             :  */
   11720         163 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11721             :                               CSLConstList papszOptions)
   11722             : {
   11723         163 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11724         163 :     VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
   11725             :     auto subGroup =
   11726         489 :         hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
   11727         163 :     if (!subGroup)
   11728          30 :         return nullptr;
   11729         133 :     return new GDALGroupHS(subGroup);
   11730             : }
   11731             : 
   11732             : /************************************************************************/
   11733             : /*                   GDALGroupGetVectorLayerNames()                     */
   11734             : /************************************************************************/
   11735             : 
   11736             : /** Return the list of layer names contained in this group.
   11737             :  *
   11738             :  * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
   11739             :  *
   11740             :  * @return the group names, to be freed with CSLDestroy()
   11741             :  * @since 3.4
   11742             :  */
   11743           8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
   11744             :                                     CSLConstList papszOptions)
   11745             : {
   11746           8 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11747          16 :     auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
   11748          16 :     CPLStringList res;
   11749          18 :     for (const auto &name : names)
   11750             :     {
   11751          10 :         res.AddString(name.c_str());
   11752             :     }
   11753           8 :     return res.StealList();
   11754             : }
   11755             : 
   11756             : /************************************************************************/
   11757             : /*                      GDALGroupOpenVectorLayer()                      */
   11758             : /************************************************************************/
   11759             : 
   11760             : /** Open and return a vector layer.
   11761             :  *
   11762             :  * This is the same as the C++ method GDALGroup::OpenVectorLayer().
   11763             :  *
   11764             :  * Note that the vector layer is owned by its parent GDALDatasetH, and thus
   11765             :  * the returned handled if only valid while the parent GDALDatasetH is kept
   11766             :  * opened.
   11767             :  *
   11768             :  * @return the vector layer, or nullptr.
   11769             :  * @since 3.4
   11770             :  */
   11771          12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
   11772             :                                    const char *pszVectorLayerName,
   11773             :                                    CSLConstList papszOptions)
   11774             : {
   11775          12 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11776          12 :     VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
   11777          24 :     return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
   11778          24 :         std::string(pszVectorLayerName), papszOptions));
   11779             : }
   11780             : 
   11781             : /************************************************************************/
   11782             : /*                       GDALGroupOpenMDArrayFromFullname()             */
   11783             : /************************************************************************/
   11784             : 
   11785             : /** Open and return a sub-group from its fully qualified name.
   11786             :  *
   11787             :  * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
   11788             :  *
   11789             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11790             :  *
   11791             :  * @since GDAL 3.2
   11792             :  */
   11793           3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
   11794             :                                           const char *pszFullname,
   11795             :                                           CSLConstList papszOptions)
   11796             : {
   11797           3 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11798           3 :     VALIDATE_POINTER1(pszFullname, __func__, nullptr);
   11799           3 :     auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
   11800           9 :         std::string(pszFullname), papszOptions);
   11801           3 :     if (!subGroup)
   11802           2 :         return nullptr;
   11803           1 :     return new GDALGroupHS(subGroup);
   11804             : }
   11805             : 
   11806             : /************************************************************************/
   11807             : /*                         GDALGroupGetDimensions()                     */
   11808             : /************************************************************************/
   11809             : 
   11810             : /** Return the list of dimensions contained in this group and used by its
   11811             :  * arrays.
   11812             :  *
   11813             :  * The returned array must be freed with GDALReleaseDimensions().  If only the
   11814             :  * array itself needs to be freed, CPLFree() should be called (and
   11815             :  * GDALDimensionRelease() on individual array members).
   11816             :  *
   11817             :  * This is the same as the C++ method GDALGroup::GetDimensions().
   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 dimensions
   11822             :  * should be retrieved. Pass nullptr for default behavior.
   11823             :  *
   11824             :  * @return an array of *pnCount dimensions.
   11825             :  */
   11826          73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
   11827             :                                        CSLConstList papszOptions)
   11828             : {
   11829          73 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11830          73 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11831          73 :     auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
   11832             :     auto ret = static_cast<GDALDimensionH *>(
   11833          73 :         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
   11834         230 :     for (size_t i = 0; i < dims.size(); i++)
   11835             :     {
   11836         157 :         ret[i] = new GDALDimensionHS(dims[i]);
   11837             :     }
   11838          73 :     *pnCount = dims.size();
   11839          73 :     return ret;
   11840             : }
   11841             : 
   11842             : /************************************************************************/
   11843             : /*                          GDALGroupGetAttribute()                     */
   11844             : /************************************************************************/
   11845             : 
   11846             : /** Return an attribute by its name.
   11847             :  *
   11848             :  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
   11849             :  *
   11850             :  * The returned attribute must be freed with GDALAttributeRelease().
   11851             :  */
   11852          80 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
   11853             : {
   11854          80 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11855          80 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11856         240 :     auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
   11857          80 :     if (attr)
   11858          76 :         return new GDALAttributeHS(attr);
   11859           4 :     return nullptr;
   11860             : }
   11861             : 
   11862             : /************************************************************************/
   11863             : /*                         GDALGroupGetAttributes()                     */
   11864             : /************************************************************************/
   11865             : 
   11866             : /** Return the list of attributes contained in this group.
   11867             :  *
   11868             :  * The returned array must be freed with GDALReleaseAttributes(). If only the
   11869             :  * array itself needs to be freed, CPLFree() should be called (and
   11870             :  * GDALAttributeRelease() on individual array members).
   11871             :  *
   11872             :  * This is the same as the C++ method GDALGroup::GetAttributes().
   11873             :  *
   11874             :  * @param hGroup Group.
   11875             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11876             :  * @param papszOptions Driver specific options determining how attributes
   11877             :  * should be retrieved. Pass nullptr for default behavior.
   11878             :  *
   11879             :  * @return an array of *pnCount attributes.
   11880             :  */
   11881          71 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
   11882             :                                        CSLConstList papszOptions)
   11883             : {
   11884          71 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11885          71 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11886          71 :     auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
   11887             :     auto ret = static_cast<GDALAttributeH *>(
   11888          71 :         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
   11889         229 :     for (size_t i = 0; i < attrs.size(); i++)
   11890             :     {
   11891         158 :         ret[i] = new GDALAttributeHS(attrs[i]);
   11892             :     }
   11893          71 :     *pnCount = attrs.size();
   11894          71 :     return ret;
   11895             : }
   11896             : 
   11897             : /************************************************************************/
   11898             : /*                     GDALGroupGetStructuralInfo()                     */
   11899             : /************************************************************************/
   11900             : 
   11901             : /** Return structural information on the group.
   11902             :  *
   11903             :  * This may be the compression, etc..
   11904             :  *
   11905             :  * The return value should not be freed and is valid until GDALGroup is
   11906             :  * released or this function called again.
   11907             :  *
   11908             :  * This is the same as the C++ method GDALGroup::GetStructuralInfo().
   11909             :  */
   11910           4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
   11911             : {
   11912           4 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11913           4 :     return hGroup->m_poImpl->GetStructuralInfo();
   11914             : }
   11915             : 
   11916             : /************************************************************************/
   11917             : /*                   GDALGroupGetDataTypeCount()                        */
   11918             : /************************************************************************/
   11919             : 
   11920             : /** Return the number of data types associated with the group
   11921             :  * (typically enumerations).
   11922             :  *
   11923             :  * This is the same as the C++ method GDALGroup::GetDataTypes().size().
   11924             :  *
   11925             :  * @since 3.12
   11926             :  */
   11927           4 : size_t GDALGroupGetDataTypeCount(GDALGroupH hGroup)
   11928             : {
   11929           4 :     VALIDATE_POINTER1(hGroup, __func__, 0);
   11930           4 :     return hGroup->m_poImpl->GetDataTypes().size();
   11931             : }
   11932             : 
   11933             : /************************************************************************/
   11934             : /*                      GDALGroupGetDataType()                          */
   11935             : /************************************************************************/
   11936             : 
   11937             : /** Return one of the data types associated with the group.
   11938             :  *
   11939             :  * This is the same as the C++ method GDALGroup::GetDataTypes()[].
   11940             :  *
   11941             :  * @return a type to release with GDALExtendedDataTypeRelease() once done,
   11942             :  * or nullptr in case of error.
   11943             :  * @since 3.12
   11944             :  */
   11945           1 : GDALExtendedDataTypeH GDALGroupGetDataType(GDALGroupH hGroup, size_t nIdx)
   11946             : {
   11947           1 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11948           1 :     if (nIdx >= hGroup->m_poImpl->GetDataTypes().size())
   11949           0 :         return nullptr;
   11950           1 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   11951           1 :         *(hGroup->m_poImpl->GetDataTypes()[nIdx].get())));
   11952             : }
   11953             : 
   11954             : /************************************************************************/
   11955             : /*                         GDALReleaseAttributes()                      */
   11956             : /************************************************************************/
   11957             : 
   11958             : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
   11959             :  *
   11960             :  * @param attributes return pointer of above methods
   11961             :  * @param nCount *pnCount value returned by above methods
   11962             :  */
   11963         130 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
   11964             : {
   11965         418 :     for (size_t i = 0; i < nCount; i++)
   11966             :     {
   11967         288 :         delete attributes[i];
   11968             :     }
   11969         130 :     CPLFree(attributes);
   11970         130 : }
   11971             : 
   11972             : /************************************************************************/
   11973             : /*                         GDALGroupCreateGroup()                       */
   11974             : /************************************************************************/
   11975             : 
   11976             : /** Create a sub-group within a group.
   11977             :  *
   11978             :  * This is the same as the C++ method GDALGroup::CreateGroup().
   11979             :  *
   11980             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11981             :  */
   11982         179 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11983             :                                 CSLConstList papszOptions)
   11984             : {
   11985         179 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11986         179 :     VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
   11987         537 :     auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
   11988         537 :                                              papszOptions);
   11989         179 :     if (!ret)
   11990          49 :         return nullptr;
   11991         130 :     return new GDALGroupHS(ret);
   11992             : }
   11993             : 
   11994             : /************************************************************************/
   11995             : /*                         GDALGroupDeleteGroup()                       */
   11996             : /************************************************************************/
   11997             : 
   11998             : /** Delete a sub-group from a group.
   11999             :  *
   12000             :  * After this call, if a previously obtained instance of the deleted object
   12001             :  * is still alive, no method other than for freeing it should be invoked.
   12002             :  *
   12003             :  * This is the same as the C++ method GDALGroup::DeleteGroup().
   12004             :  *
   12005             :  * @return true in case of success.
   12006             :  * @since GDAL 3.8
   12007             :  */
   12008          20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   12009             :                           CSLConstList papszOptions)
   12010             : {
   12011          20 :     VALIDATE_POINTER1(hGroup, __func__, false);
   12012          20 :     VALIDATE_POINTER1(pszSubGroupName, __func__, false);
   12013          40 :     return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
   12014          20 :                                          papszOptions);
   12015             : }
   12016             : 
   12017             : /************************************************************************/
   12018             : /*                      GDALGroupCreateDimension()                      */
   12019             : /************************************************************************/
   12020             : 
   12021             : /** Create a dimension within a group.
   12022             :  *
   12023             :  * This is the same as the C++ method GDALGroup::CreateDimension().
   12024             :  *
   12025             :  * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
   12026             :  */
   12027         769 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
   12028             :                                         const char *pszType,
   12029             :                                         const char *pszDirection, GUInt64 nSize,
   12030             :                                         CSLConstList papszOptions)
   12031             : {
   12032         769 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12033         769 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   12034         769 :     auto ret = hGroup->m_poImpl->CreateDimension(
   12035        1538 :         std::string(pszName), std::string(pszType ? pszType : ""),
   12036        3076 :         std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
   12037         769 :     if (!ret)
   12038           9 :         return nullptr;
   12039         760 :     return new GDALDimensionHS(ret);
   12040             : }
   12041             : 
   12042             : /************************************************************************/
   12043             : /*                      GDALGroupCreateMDArray()                        */
   12044             : /************************************************************************/
   12045             : 
   12046             : /** Create a multidimensional array within a group.
   12047             :  *
   12048             :  * This is the same as the C++ method GDALGroup::CreateMDArray().
   12049             :  *
   12050             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   12051             :  */
   12052         711 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
   12053             :                                     size_t nDimensions,
   12054             :                                     GDALDimensionH *pahDimensions,
   12055             :                                     GDALExtendedDataTypeH hEDT,
   12056             :                                     CSLConstList papszOptions)
   12057             : {
   12058         711 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12059         711 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   12060         711 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   12061        1422 :     std::vector<std::shared_ptr<GDALDimension>> dims;
   12062         711 :     dims.reserve(nDimensions);
   12063        1696 :     for (size_t i = 0; i < nDimensions; i++)
   12064         985 :         dims.push_back(pahDimensions[i]->m_poImpl);
   12065        2133 :     auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
   12066        2133 :                                                *(hEDT->m_poImpl), papszOptions);
   12067         711 :     if (!ret)
   12068          64 :         return nullptr;
   12069         647 :     return new GDALMDArrayHS(ret);
   12070             : }
   12071             : 
   12072             : /************************************************************************/
   12073             : /*                         GDALGroupDeleteMDArray()                     */
   12074             : /************************************************************************/
   12075             : 
   12076             : /** Delete an array from a group.
   12077             :  *
   12078             :  * After this call, if a previously obtained instance of the deleted object
   12079             :  * is still alive, no method other than for freeing it should be invoked.
   12080             :  *
   12081             :  * This is the same as the C++ method GDALGroup::DeleteMDArray().
   12082             :  *
   12083             :  * @return true in case of success.
   12084             :  * @since GDAL 3.8
   12085             :  */
   12086          20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
   12087             :                             CSLConstList papszOptions)
   12088             : {
   12089          20 :     VALIDATE_POINTER1(hGroup, __func__, false);
   12090          20 :     VALIDATE_POINTER1(pszName, __func__, false);
   12091          20 :     return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
   12092             : }
   12093             : 
   12094             : /************************************************************************/
   12095             : /*                      GDALGroupCreateAttribute()                      */
   12096             : /************************************************************************/
   12097             : 
   12098             : /** Create a attribute within a group.
   12099             :  *
   12100             :  * This is the same as the C++ method GDALGroup::CreateAttribute().
   12101             :  *
   12102             :  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
   12103             :  */
   12104         125 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
   12105             :                                         size_t nDimensions,
   12106             :                                         const GUInt64 *panDimensions,
   12107             :                                         GDALExtendedDataTypeH hEDT,
   12108             :                                         CSLConstList papszOptions)
   12109             : {
   12110         125 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12111         125 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   12112         250 :     std::vector<GUInt64> dims;
   12113         125 :     dims.reserve(nDimensions);
   12114         175 :     for (size_t i = 0; i < nDimensions; i++)
   12115          50 :         dims.push_back(panDimensions[i]);
   12116         125 :     auto ret = hGroup->m_poImpl->CreateAttribute(
   12117         375 :         std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
   12118         125 :     if (!ret)
   12119          14 :         return nullptr;
   12120         111 :     return new GDALAttributeHS(ret);
   12121             : }
   12122             : 
   12123             : /************************************************************************/
   12124             : /*                         GDALGroupDeleteAttribute()                   */
   12125             : /************************************************************************/
   12126             : 
   12127             : /** Delete an attribute from a group.
   12128             :  *
   12129             :  * After this call, if a previously obtained instance of the deleted object
   12130             :  * is still alive, no method other than for freeing it should be invoked.
   12131             :  *
   12132             :  * This is the same as the C++ method GDALGroup::DeleteAttribute().
   12133             :  *
   12134             :  * @return true in case of success.
   12135             :  * @since GDAL 3.8
   12136             :  */
   12137          25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
   12138             :                               CSLConstList papszOptions)
   12139             : {
   12140          25 :     VALIDATE_POINTER1(hGroup, __func__, false);
   12141          25 :     VALIDATE_POINTER1(pszName, __func__, false);
   12142          50 :     return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
   12143          25 :                                              papszOptions);
   12144             : }
   12145             : 
   12146             : /************************************************************************/
   12147             : /*                          GDALGroupRename()                           */
   12148             : /************************************************************************/
   12149             : 
   12150             : /** Rename the group.
   12151             :  *
   12152             :  * This is not implemented by all drivers.
   12153             :  *
   12154             :  * Drivers known to implement it: MEM, netCDF.
   12155             :  *
   12156             :  * This is the same as the C++ method GDALGroup::Rename()
   12157             :  *
   12158             :  * @return true in case of success
   12159             :  * @since GDAL 3.8
   12160             :  */
   12161          45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
   12162             : {
   12163          45 :     VALIDATE_POINTER1(hGroup, __func__, false);
   12164          45 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   12165          45 :     return hGroup->m_poImpl->Rename(pszNewName);
   12166             : }
   12167             : 
   12168             : /************************************************************************/
   12169             : /*                 GDALGroupSubsetDimensionFromSelection()              */
   12170             : /************************************************************************/
   12171             : 
   12172             : /** Return a virtual group whose one dimension has been subset according to a
   12173             :  * selection.
   12174             :  *
   12175             :  * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
   12176             :  *
   12177             :  * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
   12178             :  */
   12179             : GDALGroupH
   12180          14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
   12181             :                                       const char *pszSelection,
   12182             :                                       CPL_UNUSED CSLConstList papszOptions)
   12183             : {
   12184          14 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12185          14 :     VALIDATE_POINTER1(pszSelection, __func__, nullptr);
   12186          14 :     auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
   12187          42 :         std::string(pszSelection));
   12188          14 :     if (!hNewGroup)
   12189           8 :         return nullptr;
   12190           6 :     return new GDALGroupHS(hNewGroup);
   12191             : }
   12192             : 
   12193             : /************************************************************************/
   12194             : /*                        GDALMDArrayRelease()                          */
   12195             : /************************************************************************/
   12196             : 
   12197             : /** Release the GDAL in-memory object associated with a GDALMDArray.
   12198             :  *
   12199             :  * Note: when applied on a object coming from a driver, this does not
   12200             :  * destroy the object in the file, database, etc...
   12201             :  */
   12202        2193 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
   12203             : {
   12204        2193 :     delete hMDArray;
   12205        2193 : }
   12206             : 
   12207             : /************************************************************************/
   12208             : /*                        GDALMDArrayGetName()                          */
   12209             : /************************************************************************/
   12210             : 
   12211             : /** Return array name.
   12212             :  *
   12213             :  * This is the same as the C++ method GDALMDArray::GetName()
   12214             :  */
   12215          83 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
   12216             : {
   12217          83 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12218          83 :     return hArray->m_poImpl->GetName().c_str();
   12219             : }
   12220             : 
   12221             : /************************************************************************/
   12222             : /*                    GDALMDArrayGetFullName()                          */
   12223             : /************************************************************************/
   12224             : 
   12225             : /** Return array full name.
   12226             :  *
   12227             :  * This is the same as the C++ method GDALMDArray::GetFullName()
   12228             :  */
   12229          50 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
   12230             : {
   12231          50 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12232          50 :     return hArray->m_poImpl->GetFullName().c_str();
   12233             : }
   12234             : 
   12235             : /************************************************************************/
   12236             : /*                        GDALMDArrayGetName()                          */
   12237             : /************************************************************************/
   12238             : 
   12239             : /** Return the total number of values in the array.
   12240             :  *
   12241             :  * This is the same as the C++ method
   12242             :  * GDALAbstractMDArray::GetTotalElementsCount()
   12243             :  */
   12244           6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
   12245             : {
   12246           6 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12247           6 :     return hArray->m_poImpl->GetTotalElementsCount();
   12248             : }
   12249             : 
   12250             : /************************************************************************/
   12251             : /*                        GDALMDArrayGetDimensionCount()                */
   12252             : /************************************************************************/
   12253             : 
   12254             : /** Return the number of dimensions.
   12255             :  *
   12256             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
   12257             :  */
   12258       10868 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
   12259             : {
   12260       10868 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12261       10868 :     return hArray->m_poImpl->GetDimensionCount();
   12262             : }
   12263             : 
   12264             : /************************************************************************/
   12265             : /*                        GDALMDArrayGetDimensions()                    */
   12266             : /************************************************************************/
   12267             : 
   12268             : /** Return the dimensions of the array
   12269             :  *
   12270             :  * The returned array must be freed with GDALReleaseDimensions(). If only the
   12271             :  * array itself needs to be freed, CPLFree() should be called (and
   12272             :  * GDALDimensionRelease() on individual array members).
   12273             :  *
   12274             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
   12275             :  *
   12276             :  * @param hArray Array.
   12277             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12278             :  *
   12279             :  * @return an array of *pnCount dimensions.
   12280             :  */
   12281        2458 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
   12282             : {
   12283        2458 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12284        2458 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12285        2458 :     const auto &dims(hArray->m_poImpl->GetDimensions());
   12286             :     auto ret = static_cast<GDALDimensionH *>(
   12287        2458 :         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
   12288        6935 :     for (size_t i = 0; i < dims.size(); i++)
   12289             :     {
   12290        4477 :         ret[i] = new GDALDimensionHS(dims[i]);
   12291             :     }
   12292        2458 :     *pnCount = dims.size();
   12293        2458 :     return ret;
   12294             : }
   12295             : 
   12296             : /************************************************************************/
   12297             : /*                        GDALReleaseDimensions()                       */
   12298             : /************************************************************************/
   12299             : 
   12300             : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
   12301             :  *
   12302             :  * @param dims return pointer of above methods
   12303             :  * @param nCount *pnCount value returned by above methods
   12304             :  */
   12305        2531 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
   12306             : {
   12307        7165 :     for (size_t i = 0; i < nCount; i++)
   12308             :     {
   12309        4634 :         delete dims[i];
   12310             :     }
   12311        2531 :     CPLFree(dims);
   12312        2531 : }
   12313             : 
   12314             : /************************************************************************/
   12315             : /*                        GDALMDArrayGetDataType()                     */
   12316             : /************************************************************************/
   12317             : 
   12318             : /** Return the data type
   12319             :  *
   12320             :  * The return must be freed with GDALExtendedDataTypeRelease().
   12321             :  */
   12322        4192 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
   12323             : {
   12324        4192 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12325             :     return new GDALExtendedDataTypeHS(
   12326        4192 :         new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
   12327             : }
   12328             : 
   12329             : /************************************************************************/
   12330             : /*                          GDALMDArrayRead()                           */
   12331             : /************************************************************************/
   12332             : 
   12333             : /** Read part or totality of a multidimensional array.
   12334             :  *
   12335             :  * This is the same as the C++ method GDALAbstractMDArray::Read()
   12336             :  *
   12337             :  * @return TRUE in case of success.
   12338             :  */
   12339        1993 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   12340             :                     const size_t *count, const GInt64 *arrayStep,
   12341             :                     const GPtrDiff_t *bufferStride,
   12342             :                     GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
   12343             :                     const void *pDstBufferAllocStart,
   12344             :                     size_t nDstBufferAllocSize)
   12345             : {
   12346        1993 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12347        1993 :     if ((arrayStartIdx == nullptr || count == nullptr) &&
   12348           0 :         hArray->m_poImpl->GetDimensionCount() > 0)
   12349             :     {
   12350           0 :         VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
   12351           0 :         VALIDATE_POINTER1(count, __func__, FALSE);
   12352             :     }
   12353        1993 :     VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
   12354        1993 :     VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
   12355        3986 :     return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
   12356        1993 :                                   *(bufferDataType->m_poImpl), pDstBuffer,
   12357        1993 :                                   pDstBufferAllocStart, nDstBufferAllocSize);
   12358             : }
   12359             : 
   12360             : /************************************************************************/
   12361             : /*                          GDALMDArrayWrite()                           */
   12362             : /************************************************************************/
   12363             : 
   12364             : /** Write part or totality of a multidimensional array.
   12365             :  *
   12366             :  * This is the same as the C++ method GDALAbstractMDArray::Write()
   12367             :  *
   12368             :  * @return TRUE in case of success.
   12369             :  */
   12370         657 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   12371             :                      const size_t *count, const GInt64 *arrayStep,
   12372             :                      const GPtrDiff_t *bufferStride,
   12373             :                      GDALExtendedDataTypeH bufferDataType,
   12374             :                      const void *pSrcBuffer, const void *pSrcBufferAllocStart,
   12375             :                      size_t nSrcBufferAllocSize)
   12376             : {
   12377         657 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12378         657 :     if ((arrayStartIdx == nullptr || count == nullptr) &&
   12379           0 :         hArray->m_poImpl->GetDimensionCount() > 0)
   12380             :     {
   12381           0 :         VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
   12382           0 :         VALIDATE_POINTER1(count, __func__, FALSE);
   12383             :     }
   12384         657 :     VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
   12385         657 :     VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
   12386        1314 :     return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
   12387         657 :                                    bufferStride, *(bufferDataType->m_poImpl),
   12388             :                                    pSrcBuffer, pSrcBufferAllocStart,
   12389         657 :                                    nSrcBufferAllocSize);
   12390             : }
   12391             : 
   12392             : /************************************************************************/
   12393             : /*                       GDALMDArrayAdviseRead()                        */
   12394             : /************************************************************************/
   12395             : 
   12396             : /** Advise driver of upcoming read requests.
   12397             :  *
   12398             :  * This is the same as the C++ method GDALMDArray::AdviseRead()
   12399             :  *
   12400             :  * @return TRUE in case of success.
   12401             :  *
   12402             :  * @since GDAL 3.2
   12403             :  */
   12404           0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   12405             :                           const size_t *count)
   12406             : {
   12407           0 :     return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
   12408             : }
   12409             : 
   12410             : /************************************************************************/
   12411             : /*                      GDALMDArrayAdviseReadEx()                       */
   12412             : /************************************************************************/
   12413             : 
   12414             : /** Advise driver of upcoming read requests.
   12415             :  *
   12416             :  * This is the same as the C++ method GDALMDArray::AdviseRead()
   12417             :  *
   12418             :  * @return TRUE in case of success.
   12419             :  *
   12420             :  * @since GDAL 3.4
   12421             :  */
   12422          22 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   12423             :                             const size_t *count, CSLConstList papszOptions)
   12424             : {
   12425          22 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12426          22 :     return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
   12427             : }
   12428             : 
   12429             : /************************************************************************/
   12430             : /*                         GDALMDArrayGetAttribute()                    */
   12431             : /************************************************************************/
   12432             : 
   12433             : /** Return an attribute by its name.
   12434             :  *
   12435             :  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
   12436             :  *
   12437             :  * The returned attribute must be freed with GDALAttributeRelease().
   12438             :  */
   12439         120 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
   12440             : {
   12441         120 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12442         120 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   12443         360 :     auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
   12444         120 :     if (attr)
   12445         111 :         return new GDALAttributeHS(attr);
   12446           9 :     return nullptr;
   12447             : }
   12448             : 
   12449             : /************************************************************************/
   12450             : /*                        GDALMDArrayGetAttributes()                    */
   12451             : /************************************************************************/
   12452             : 
   12453             : /** Return the list of attributes contained in this array.
   12454             :  *
   12455             :  * The returned array must be freed with GDALReleaseAttributes(). If only the
   12456             :  * array itself needs to be freed, CPLFree() should be called (and
   12457             :  * GDALAttributeRelease() on individual array members).
   12458             :  *
   12459             :  * This is the same as the C++ method GDALMDArray::GetAttributes().
   12460             :  *
   12461             :  * @param hArray Array.
   12462             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12463             :  * @param papszOptions Driver specific options determining how attributes
   12464             :  * should be retrieved. Pass nullptr for default behavior.
   12465             :  *
   12466             :  * @return an array of *pnCount attributes.
   12467             :  */
   12468          59 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
   12469             :                                          CSLConstList papszOptions)
   12470             : {
   12471          59 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12472          59 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12473          59 :     auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
   12474             :     auto ret = static_cast<GDALAttributeH *>(
   12475          59 :         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
   12476         189 :     for (size_t i = 0; i < attrs.size(); i++)
   12477             :     {
   12478         130 :         ret[i] = new GDALAttributeHS(attrs[i]);
   12479             :     }
   12480          59 :     *pnCount = attrs.size();
   12481          59 :     return ret;
   12482             : }
   12483             : 
   12484             : /************************************************************************/
   12485             : /*                       GDALMDArrayCreateAttribute()                   */
   12486             : /************************************************************************/
   12487             : 
   12488             : /** Create a attribute within an array.
   12489             :  *
   12490             :  * This is the same as the C++ method GDALMDArray::CreateAttribute().
   12491             :  *
   12492             :  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
   12493             :  */
   12494         188 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
   12495             :                                           const char *pszName,
   12496             :                                           size_t nDimensions,
   12497             :                                           const GUInt64 *panDimensions,
   12498             :                                           GDALExtendedDataTypeH hEDT,
   12499             :                                           CSLConstList papszOptions)
   12500             : {
   12501         188 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12502         188 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   12503         188 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   12504         376 :     std::vector<GUInt64> dims;
   12505         188 :     dims.reserve(nDimensions);
   12506         249 :     for (size_t i = 0; i < nDimensions; i++)
   12507          61 :         dims.push_back(panDimensions[i]);
   12508         188 :     auto ret = hArray->m_poImpl->CreateAttribute(
   12509         564 :         std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
   12510         188 :     if (!ret)
   12511           9 :         return nullptr;
   12512         179 :     return new GDALAttributeHS(ret);
   12513             : }
   12514             : 
   12515             : /************************************************************************/
   12516             : /*                       GDALMDArrayDeleteAttribute()                   */
   12517             : /************************************************************************/
   12518             : 
   12519             : /** Delete an attribute from an array.
   12520             :  *
   12521             :  * After this call, if a previously obtained instance of the deleted object
   12522             :  * is still alive, no method other than for freeing it should be invoked.
   12523             :  *
   12524             :  * This is the same as the C++ method GDALMDArray::DeleteAttribute().
   12525             :  *
   12526             :  * @return true in case of success.
   12527             :  * @since GDAL 3.8
   12528             :  */
   12529          24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
   12530             :                                 CSLConstList papszOptions)
   12531             : {
   12532          24 :     VALIDATE_POINTER1(hArray, __func__, false);
   12533          24 :     VALIDATE_POINTER1(pszName, __func__, false);
   12534          48 :     return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
   12535          24 :                                              papszOptions);
   12536             : }
   12537             : 
   12538             : /************************************************************************/
   12539             : /*                       GDALMDArrayGetRawNoDataValue()                 */
   12540             : /************************************************************************/
   12541             : 
   12542             : /** Return the nodata value as a "raw" value.
   12543             :  *
   12544             :  * The value returned might be nullptr in case of no nodata value. When
   12545             :  * a nodata value is registered, a non-nullptr will be returned whose size in
   12546             :  * bytes is GetDataType().GetSize().
   12547             :  *
   12548             :  * The returned value should not be modified or freed.
   12549             :  *
   12550             :  * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
   12551             :  *
   12552             :  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
   12553             :  */
   12554          77 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
   12555             : {
   12556          77 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12557          77 :     return hArray->m_poImpl->GetRawNoDataValue();
   12558             : }
   12559             : 
   12560             : /************************************************************************/
   12561             : /*                      GDALMDArrayGetNoDataValueAsDouble()             */
   12562             : /************************************************************************/
   12563             : 
   12564             : /** Return the nodata value as a double.
   12565             :  *
   12566             :  * The value returned might be nullptr in case of no nodata value. When
   12567             :  * a nodata value is registered, a non-nullptr will be returned whose size in
   12568             :  * bytes is GetDataType().GetSize().
   12569             :  *
   12570             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
   12571             :  *
   12572             :  * @param hArray Array handle.
   12573             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12574             :  * if a nodata value exists and can be converted to double. Might be nullptr.
   12575             :  *
   12576             :  * @return the nodata value as a double. A 0.0 value might also indicate the
   12577             :  * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
   12578             :  * will be set to false then).
   12579             :  */
   12580         121 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
   12581             :                                          int *pbHasNoDataValue)
   12582             : {
   12583         121 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12584         121 :     bool bHasNodataValue = false;
   12585         121 :     double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
   12586         121 :     if (pbHasNoDataValue)
   12587         121 :         *pbHasNoDataValue = bHasNodataValue;
   12588         121 :     return ret;
   12589             : }
   12590             : 
   12591             : /************************************************************************/
   12592             : /*                      GDALMDArrayGetNoDataValueAsInt64()              */
   12593             : /************************************************************************/
   12594             : 
   12595             : /** Return the nodata value as a Int64.
   12596             :  *
   12597             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
   12598             :  *
   12599             :  * @param hArray Array handle.
   12600             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12601             :  * if a nodata value exists and can be converted to Int64. Might be nullptr.
   12602             :  *
   12603             :  * @return the nodata value as a Int64.
   12604             :  * @since GDAL 3.5
   12605             :  */
   12606          11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
   12607             :                                          int *pbHasNoDataValue)
   12608             : {
   12609          11 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12610          11 :     bool bHasNodataValue = false;
   12611          11 :     const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
   12612          11 :     if (pbHasNoDataValue)
   12613          11 :         *pbHasNoDataValue = bHasNodataValue;
   12614          11 :     return ret;
   12615             : }
   12616             : 
   12617             : /************************************************************************/
   12618             : /*                      GDALMDArrayGetNoDataValueAsUInt64()              */
   12619             : /************************************************************************/
   12620             : 
   12621             : /** Return the nodata value as a UInt64.
   12622             :  *
   12623             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
   12624             :  *
   12625             :  * @param hArray Array handle.
   12626             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12627             :  * if a nodata value exists and can be converted to UInt64. Might be nullptr.
   12628             :  *
   12629             :  * @return the nodata value as a UInt64.
   12630             :  * @since GDAL 3.5
   12631             :  */
   12632           7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
   12633             :                                            int *pbHasNoDataValue)
   12634             : {
   12635           7 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12636           7 :     bool bHasNodataValue = false;
   12637           7 :     const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
   12638           7 :     if (pbHasNoDataValue)
   12639           7 :         *pbHasNoDataValue = bHasNodataValue;
   12640           7 :     return ret;
   12641             : }
   12642             : 
   12643             : /************************************************************************/
   12644             : /*                     GDALMDArraySetRawNoDataValue()                   */
   12645             : /************************************************************************/
   12646             : 
   12647             : /** Set the nodata value as a "raw" value.
   12648             :  *
   12649             :  * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
   12650             :  * void*).
   12651             :  *
   12652             :  * @return TRUE in case of success.
   12653             :  */
   12654          14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
   12655             : {
   12656          14 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12657          14 :     return hArray->m_poImpl->SetRawNoDataValue(pNoData);
   12658             : }
   12659             : 
   12660             : /************************************************************************/
   12661             : /*                   GDALMDArraySetNoDataValueAsDouble()                */
   12662             : /************************************************************************/
   12663             : 
   12664             : /** Set the nodata value as a double.
   12665             :  *
   12666             :  * If the natural data type of the attribute/array is not double, type
   12667             :  * conversion will occur to the type returned by GetDataType().
   12668             :  *
   12669             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
   12670             :  *
   12671             :  * @return TRUE in case of success.
   12672             :  */
   12673          55 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
   12674             : {
   12675          55 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12676          55 :     return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
   12677             : }
   12678             : 
   12679             : /************************************************************************/
   12680             : /*                   GDALMDArraySetNoDataValueAsInt64()                 */
   12681             : /************************************************************************/
   12682             : 
   12683             : /** Set the nodata value as a Int64.
   12684             :  *
   12685             :  * If the natural data type of the attribute/array is not Int64, type conversion
   12686             :  * will occur to the type returned by GetDataType().
   12687             :  *
   12688             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
   12689             :  *
   12690             :  * @return TRUE in case of success.
   12691             :  * @since GDAL 3.5
   12692             :  */
   12693           1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
   12694             : {
   12695           1 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12696           1 :     return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
   12697             : }
   12698             : 
   12699             : /************************************************************************/
   12700             : /*                   GDALMDArraySetNoDataValueAsUInt64()                */
   12701             : /************************************************************************/
   12702             : 
   12703             : /** Set the nodata value as a UInt64.
   12704             :  *
   12705             :  * If the natural data type of the attribute/array is not UInt64, type
   12706             :  * conversion will occur to the type returned by GetDataType().
   12707             :  *
   12708             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
   12709             :  *
   12710             :  * @return TRUE in case of success.
   12711             :  * @since GDAL 3.5
   12712             :  */
   12713           1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
   12714             :                                       uint64_t nNoDataValue)
   12715             : {
   12716           1 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12717           1 :     return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
   12718             : }
   12719             : 
   12720             : /************************************************************************/
   12721             : /*                        GDALMDArrayResize()                           */
   12722             : /************************************************************************/
   12723             : 
   12724             : /** Resize an array to new dimensions.
   12725             :  *
   12726             :  * Not all drivers may allow this operation, and with restrictions (e.g.
   12727             :  * for netCDF, this is limited to growing of "unlimited" dimensions)
   12728             :  *
   12729             :  * Resizing a dimension used in other arrays will cause those other arrays
   12730             :  * to be resized.
   12731             :  *
   12732             :  * This is the same as the C++ method GDALMDArray::Resize().
   12733             :  *
   12734             :  * @param hArray Array.
   12735             :  * @param panNewDimSizes Array of GetDimensionCount() values containing the
   12736             :  *                       new size of each indexing dimension.
   12737             :  * @param papszOptions Options. (Driver specific)
   12738             :  * @return true in case of success.
   12739             :  * @since GDAL 3.7
   12740             :  */
   12741          42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
   12742             :                        CSLConstList papszOptions)
   12743             : {
   12744          42 :     VALIDATE_POINTER1(hArray, __func__, false);
   12745          42 :     VALIDATE_POINTER1(panNewDimSizes, __func__, false);
   12746          84 :     std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
   12747         125 :     for (size_t i = 0; i < anNewDimSizes.size(); ++i)
   12748             :     {
   12749          83 :         anNewDimSizes[i] = panNewDimSizes[i];
   12750             :     }
   12751          42 :     return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
   12752             : }
   12753             : 
   12754             : /************************************************************************/
   12755             : /*                          GDALMDArraySetScale()                       */
   12756             : /************************************************************************/
   12757             : 
   12758             : /** Set the scale value to apply to raw values.
   12759             :  *
   12760             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12761             :  *
   12762             :  * This is the same as the C++ method GDALMDArray::SetScale().
   12763             :  *
   12764             :  * @return TRUE in case of success.
   12765             :  */
   12766           0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
   12767             : {
   12768           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12769           0 :     return hArray->m_poImpl->SetScale(dfScale);
   12770             : }
   12771             : 
   12772             : /************************************************************************/
   12773             : /*                        GDALMDArraySetScaleEx()                       */
   12774             : /************************************************************************/
   12775             : 
   12776             : /** Set the scale value to apply to raw values.
   12777             :  *
   12778             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12779             :  *
   12780             :  * This is the same as the C++ method GDALMDArray::SetScale().
   12781             :  *
   12782             :  * @return TRUE in case of success.
   12783             :  * @since GDAL 3.3
   12784             :  */
   12785          21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
   12786             :                           GDALDataType eStorageType)
   12787             : {
   12788          21 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12789          21 :     return hArray->m_poImpl->SetScale(dfScale, eStorageType);
   12790             : }
   12791             : 
   12792             : /************************************************************************/
   12793             : /*                          GDALMDArraySetOffset()                       */
   12794             : /************************************************************************/
   12795             : 
   12796             : /** Set the scale value to apply to raw values.
   12797             :  *
   12798             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12799             :  *
   12800             :  * This is the same as the C++ method GDALMDArray::SetOffset().
   12801             :  *
   12802             :  * @return TRUE in case of success.
   12803             :  */
   12804           0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
   12805             : {
   12806           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12807           0 :     return hArray->m_poImpl->SetOffset(dfOffset);
   12808             : }
   12809             : 
   12810             : /************************************************************************/
   12811             : /*                       GDALMDArraySetOffsetEx()                       */
   12812             : /************************************************************************/
   12813             : 
   12814             : /** Set the scale value to apply to raw values.
   12815             :  *
   12816             :  * unscaled_value = raw_value * GetOffset() + GetOffset()
   12817             :  *
   12818             :  * This is the same as the C++ method GDALMDArray::SetOffset().
   12819             :  *
   12820             :  * @return TRUE in case of success.
   12821             :  * @since GDAL 3.3
   12822             :  */
   12823          21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
   12824             :                            GDALDataType eStorageType)
   12825             : {
   12826          21 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12827          21 :     return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
   12828             : }
   12829             : 
   12830             : /************************************************************************/
   12831             : /*                          GDALMDArrayGetScale()                       */
   12832             : /************************************************************************/
   12833             : 
   12834             : /** Get the scale value to apply to raw values.
   12835             :  *
   12836             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12837             :  *
   12838             :  * This is the same as the C++ method GDALMDArray::GetScale().
   12839             :  *
   12840             :  * @return the scale value
   12841             :  */
   12842         105 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
   12843             : {
   12844         105 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12845         105 :     bool bHasValue = false;
   12846         105 :     double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
   12847         105 :     if (pbHasValue)
   12848         105 :         *pbHasValue = bHasValue;
   12849         105 :     return dfRet;
   12850             : }
   12851             : 
   12852             : /************************************************************************/
   12853             : /*                        GDALMDArrayGetScaleEx()                       */
   12854             : /************************************************************************/
   12855             : 
   12856             : /** Get the scale value to apply to raw values.
   12857             :  *
   12858             :  * unscaled_value = raw_value * GetScale() + GetScale()
   12859             :  *
   12860             :  * This is the same as the C++ method GDALMDArray::GetScale().
   12861             :  *
   12862             :  * @return the scale value
   12863             :  * @since GDAL 3.3
   12864             :  */
   12865           5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
   12866             :                              GDALDataType *peStorageType)
   12867             : {
   12868           5 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12869           5 :     bool bHasValue = false;
   12870           5 :     double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
   12871           5 :     if (pbHasValue)
   12872           5 :         *pbHasValue = bHasValue;
   12873           5 :     return dfRet;
   12874             : }
   12875             : 
   12876             : /************************************************************************/
   12877             : /*                          GDALMDArrayGetOffset()                      */
   12878             : /************************************************************************/
   12879             : 
   12880             : /** Get the scale value to apply to raw values.
   12881             :  *
   12882             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12883             :  *
   12884             :  * This is the same as the C++ method GDALMDArray::GetOffset().
   12885             :  *
   12886             :  * @return the scale value
   12887             :  */
   12888         102 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
   12889             : {
   12890         102 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12891         102 :     bool bHasValue = false;
   12892         102 :     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
   12893         102 :     if (pbHasValue)
   12894         102 :         *pbHasValue = bHasValue;
   12895         102 :     return dfRet;
   12896             : }
   12897             : 
   12898             : /************************************************************************/
   12899             : /*                        GDALMDArrayGetOffsetEx()                      */
   12900             : /************************************************************************/
   12901             : 
   12902             : /** Get the scale value to apply to raw values.
   12903             :  *
   12904             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12905             :  *
   12906             :  * This is the same as the C++ method GDALMDArray::GetOffset().
   12907             :  *
   12908             :  * @return the scale value
   12909             :  * @since GDAL 3.3
   12910             :  */
   12911           5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
   12912             :                               GDALDataType *peStorageType)
   12913             : {
   12914           5 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12915           5 :     bool bHasValue = false;
   12916           5 :     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
   12917           5 :     if (pbHasValue)
   12918           5 :         *pbHasValue = bHasValue;
   12919           5 :     return dfRet;
   12920             : }
   12921             : 
   12922             : /************************************************************************/
   12923             : /*                      GDALMDArrayGetBlockSize()                       */
   12924             : /************************************************************************/
   12925             : 
   12926             : /** Return the "natural" block size of the array along all dimensions.
   12927             :  *
   12928             :  * Some drivers might organize the array in tiles/blocks and reading/writing
   12929             :  * aligned on those tile/block boundaries will be more efficient.
   12930             :  *
   12931             :  * The returned number of elements in the vector is the same as
   12932             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
   12933             :  * the natural block size along the considered dimension.
   12934             :  * "Flat" arrays will typically return a vector of values set to 0.
   12935             :  *
   12936             :  * The default implementation will return a vector of values set to 0.
   12937             :  *
   12938             :  * This method is used by GetProcessingChunkSize().
   12939             :  *
   12940             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikely
   12941             :  * theoretical case of a 32-bit platform, this might exceed its size_t
   12942             :  * allocation capabilities.
   12943             :  *
   12944             :  * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
   12945             :  *
   12946             :  * @return the block size, in number of elements along each dimension.
   12947             :  */
   12948          98 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
   12949             : {
   12950          98 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12951          98 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12952          98 :     auto res = hArray->m_poImpl->GetBlockSize();
   12953          98 :     auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
   12954         301 :     for (size_t i = 0; i < res.size(); i++)
   12955             :     {
   12956         203 :         ret[i] = res[i];
   12957             :     }
   12958          98 :     *pnCount = res.size();
   12959          98 :     return ret;
   12960             : }
   12961             : 
   12962             : /***********************************************************************/
   12963             : /*                   GDALMDArrayGetProcessingChunkSize()               */
   12964             : /************************************************************************/
   12965             : 
   12966             : /** \brief Return an optimal chunk size for read/write operations, given the
   12967             :  * natural block size and memory constraints specified.
   12968             :  *
   12969             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
   12970             :  * multiple of those returned by GetBlockSize() (unless the block define by
   12971             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
   12972             :  * returned by this method).
   12973             :  *
   12974             :  * This is the same as the C++ method
   12975             :  * GDALAbstractMDArray::GetProcessingChunkSize().
   12976             :  *
   12977             :  * @param hArray Array.
   12978             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12979             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
   12980             :  * chunk.
   12981             :  *
   12982             :  * @return the chunk size, in number of elements along each dimension.
   12983             :  */
   12984             : 
   12985           1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
   12986             :                                           size_t nMaxChunkMemory)
   12987             : {
   12988           1 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12989           1 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12990           1 :     auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
   12991           1 :     auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
   12992           3 :     for (size_t i = 0; i < res.size(); i++)
   12993             :     {
   12994           2 :         ret[i] = res[i];
   12995             :     }
   12996           1 :     *pnCount = res.size();
   12997           1 :     return ret;
   12998             : }
   12999             : 
   13000             : /************************************************************************/
   13001             : /*                     GDALMDArrayGetStructuralInfo()                   */
   13002             : /************************************************************************/
   13003             : 
   13004             : /** Return structural information on the array.
   13005             :  *
   13006             :  * This may be the compression, etc..
   13007             :  *
   13008             :  * The return value should not be freed and is valid until GDALMDArray is
   13009             :  * released or this function called again.
   13010             :  *
   13011             :  * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
   13012             :  */
   13013          15 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
   13014             : {
   13015          15 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13016          15 :     return hArray->m_poImpl->GetStructuralInfo();
   13017             : }
   13018             : 
   13019             : /************************************************************************/
   13020             : /*                        GDALMDArrayGetView()                          */
   13021             : /************************************************************************/
   13022             : 
   13023             : /** Return a view of the array using slicing or field access.
   13024             :  *
   13025             :  * The returned object should be released with GDALMDArrayRelease().
   13026             :  *
   13027             :  * This is the same as the C++ method GDALMDArray::GetView().
   13028             :  */
   13029         438 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
   13030             : {
   13031         438 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13032         438 :     VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
   13033        1314 :     auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
   13034         438 :     if (!sliced)
   13035          23 :         return nullptr;
   13036         415 :     return new GDALMDArrayHS(sliced);
   13037             : }
   13038             : 
   13039             : /************************************************************************/
   13040             : /*                       GDALMDArrayTranspose()                         */
   13041             : /************************************************************************/
   13042             : 
   13043             : /** Return a view of the array whose axis have been reordered.
   13044             :  *
   13045             :  * The returned object should be released with GDALMDArrayRelease().
   13046             :  *
   13047             :  * This is the same as the C++ method GDALMDArray::Transpose().
   13048             :  */
   13049          44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
   13050             :                                   const int *panMapNewAxisToOldAxis)
   13051             : {
   13052          44 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13053          88 :     std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
   13054          44 :     if (nNewAxisCount)
   13055             :     {
   13056          43 :         memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
   13057             :                nNewAxisCount * sizeof(int));
   13058             :     }
   13059          88 :     auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
   13060          44 :     if (!reordered)
   13061           7 :         return nullptr;
   13062          37 :     return new GDALMDArrayHS(reordered);
   13063             : }
   13064             : 
   13065             : /************************************************************************/
   13066             : /*                      GDALMDArrayGetUnscaled()                        */
   13067             : /************************************************************************/
   13068             : 
   13069             : /** Return an array that is the unscaled version of the current one.
   13070             :  *
   13071             :  * That is each value of the unscaled array will be
   13072             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   13073             :  *
   13074             :  * Starting with GDAL 3.3, the Write() method is implemented and will convert
   13075             :  * from unscaled values to raw values.
   13076             :  *
   13077             :  * The returned object should be released with GDALMDArrayRelease().
   13078             :  *
   13079             :  * This is the same as the C++ method GDALMDArray::GetUnscaled().
   13080             :  */
   13081          13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
   13082             : {
   13083          13 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13084          26 :     auto unscaled = hArray->m_poImpl->GetUnscaled();
   13085          13 :     if (!unscaled)
   13086           0 :         return nullptr;
   13087          13 :     return new GDALMDArrayHS(unscaled);
   13088             : }
   13089             : 
   13090             : /************************************************************************/
   13091             : /*                          GDALMDArrayGetMask()                         */
   13092             : /************************************************************************/
   13093             : 
   13094             : /** Return an array that is a mask for the current array
   13095             :  *
   13096             :  * This array will be of type Byte, with values set to 0 to indicate invalid
   13097             :  * pixels of the current array, and values set to 1 to indicate valid pixels.
   13098             :  *
   13099             :  * The returned object should be released with GDALMDArrayRelease().
   13100             :  *
   13101             :  * This is the same as the C++ method GDALMDArray::GetMask().
   13102             :  */
   13103          35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
   13104             : {
   13105          35 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13106          70 :     auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
   13107          35 :     if (!unscaled)
   13108           7 :         return nullptr;
   13109          28 :     return new GDALMDArrayHS(unscaled);
   13110             : }
   13111             : 
   13112             : /************************************************************************/
   13113             : /*                   GDALMDArrayGetResampled()                          */
   13114             : /************************************************************************/
   13115             : 
   13116             : /** Return an array that is a resampled / reprojected view of the current array
   13117             :  *
   13118             :  * This is the same as the C++ method GDALMDArray::GetResampled().
   13119             :  *
   13120             :  * Currently this method can only resample along the last 2 dimensions, unless
   13121             :  * orthorectifying a NASA EMIT dataset.
   13122             :  *
   13123             :  * The returned object should be released with GDALMDArrayRelease().
   13124             :  *
   13125             :  * @since 3.4
   13126             :  */
   13127          34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
   13128             :                                      const GDALDimensionH *pahNewDims,
   13129             :                                      GDALRIOResampleAlg resampleAlg,
   13130             :                                      OGRSpatialReferenceH hTargetSRS,
   13131             :                                      CSLConstList papszOptions)
   13132             : {
   13133          34 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13134          34 :     VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
   13135          68 :     std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
   13136         112 :     for (size_t i = 0; i < nNewDimCount; ++i)
   13137             :     {
   13138          78 :         if (pahNewDims[i])
   13139           8 :             apoNewDims[i] = pahNewDims[i]->m_poImpl;
   13140             :     }
   13141          34 :     auto poNewArray = hArray->m_poImpl->GetResampled(
   13142          34 :         apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
   13143          68 :         papszOptions);
   13144          34 :     if (!poNewArray)
   13145           8 :         return nullptr;
   13146          26 :     return new GDALMDArrayHS(poNewArray);
   13147             : }
   13148             : 
   13149             : /************************************************************************/
   13150             : /*                      GDALMDArraySetUnit()                            */
   13151             : /************************************************************************/
   13152             : 
   13153             : /** Set the variable unit.
   13154             :  *
   13155             :  * Values should conform as much as possible with those allowed by
   13156             :  * the NetCDF CF conventions:
   13157             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
   13158             :  * but others might be returned.
   13159             :  *
   13160             :  * Few examples are "meter", "degrees", "second", ...
   13161             :  * Empty value means unknown.
   13162             :  *
   13163             :  * This is the same as the C function GDALMDArraySetUnit()
   13164             :  *
   13165             :  * @param hArray array.
   13166             :  * @param pszUnit unit name.
   13167             :  * @return TRUE in case of success.
   13168             :  */
   13169          15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
   13170             : {
   13171          15 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13172          15 :     return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
   13173             : }
   13174             : 
   13175             : /************************************************************************/
   13176             : /*                      GDALMDArrayGetUnit()                            */
   13177             : /************************************************************************/
   13178             : 
   13179             : /** Return the array unit.
   13180             :  *
   13181             :  * Values should conform as much as possible with those allowed by
   13182             :  * the NetCDF CF conventions:
   13183             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
   13184             :  * but others might be returned.
   13185             :  *
   13186             :  * Few examples are "meter", "degrees", "second", ...
   13187             :  * Empty value means unknown.
   13188             :  *
   13189             :  * The return value should not be freed and is valid until GDALMDArray is
   13190             :  * released or this function called again.
   13191             :  *
   13192             :  * This is the same as the C++ method GDALMDArray::GetUnit().
   13193             :  */
   13194         113 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
   13195             : {
   13196         113 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13197         113 :     return hArray->m_poImpl->GetUnit().c_str();
   13198             : }
   13199             : 
   13200             : /************************************************************************/
   13201             : /*                      GDALMDArrayGetSpatialRef()                      */
   13202             : /************************************************************************/
   13203             : 
   13204             : /** Assign a spatial reference system object to the array.
   13205             :  *
   13206             :  * This is the same as the C++ method GDALMDArray::SetSpatialRef().
   13207             :  * @return TRUE in case of success.
   13208             :  */
   13209          30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
   13210             : {
   13211          30 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13212          60 :     return hArray->m_poImpl->SetSpatialRef(
   13213          60 :         OGRSpatialReference::FromHandle(hSRS));
   13214             : }
   13215             : 
   13216             : /************************************************************************/
   13217             : /*                      GDALMDArrayGetSpatialRef()                      */
   13218             : /************************************************************************/
   13219             : 
   13220             : /** Return the spatial reference system object associated with the array.
   13221             :  *
   13222             :  * This is the same as the C++ method GDALMDArray::GetSpatialRef().
   13223             :  *
   13224             :  * The returned object must be freed with OSRDestroySpatialReference().
   13225             :  */
   13226          81 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
   13227             : {
   13228          81 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13229          81 :     auto poSRS = hArray->m_poImpl->GetSpatialRef();
   13230          81 :     return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
   13231             : }
   13232             : 
   13233             : /************************************************************************/
   13234             : /*                      GDALMDArrayGetStatistics()                      */
   13235             : /************************************************************************/
   13236             : 
   13237             : /**
   13238             :  * \brief Fetch statistics.
   13239             :  *
   13240             :  * This is the same as the C++ method GDALMDArray::GetStatistics().
   13241             :  *
   13242             :  * @since GDAL 3.2
   13243             :  */
   13244             : 
   13245          15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
   13246             :                                 int bApproxOK, int bForce, double *pdfMin,
   13247             :                                 double *pdfMax, double *pdfMean,
   13248             :                                 double *pdfStdDev, GUInt64 *pnValidCount,
   13249             :                                 GDALProgressFunc pfnProgress,
   13250             :                                 void *pProgressData)
   13251             : {
   13252          15 :     VALIDATE_POINTER1(hArray, __func__, CE_Failure);
   13253          30 :     return hArray->m_poImpl->GetStatistics(
   13254          15 :         CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
   13255          15 :         pdfStdDev, pnValidCount, pfnProgress, pProgressData);
   13256             : }
   13257             : 
   13258             : /************************************************************************/
   13259             : /*                      GDALMDArrayComputeStatistics()                  */
   13260             : /************************************************************************/
   13261             : 
   13262             : /**
   13263             :  * \brief Compute statistics.
   13264             :  *
   13265             :  * This is the same as the C++ method GDALMDArray::ComputeStatistics().
   13266             :  *
   13267             :  * @since GDAL 3.2
   13268             :  * @see GDALMDArrayComputeStatisticsEx()
   13269             :  */
   13270             : 
   13271           0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
   13272             :                                  int bApproxOK, double *pdfMin, double *pdfMax,
   13273             :                                  double *pdfMean, double *pdfStdDev,
   13274             :                                  GUInt64 *pnValidCount,
   13275             :                                  GDALProgressFunc pfnProgress,
   13276             :                                  void *pProgressData)
   13277             : {
   13278           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13279           0 :     return hArray->m_poImpl->ComputeStatistics(
   13280           0 :         CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
   13281           0 :         pnValidCount, pfnProgress, pProgressData, nullptr);
   13282             : }
   13283             : 
   13284             : /************************************************************************/
   13285             : /*                     GDALMDArrayComputeStatisticsEx()                 */
   13286             : /************************************************************************/
   13287             : 
   13288             : /**
   13289             :  * \brief Compute statistics.
   13290             :  *
   13291             :  * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
   13292             :  *
   13293             :  * This is the same as the C++ method GDALMDArray::ComputeStatistics().
   13294             :  *
   13295             :  * @since GDAL 3.8
   13296             :  */
   13297             : 
   13298           4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
   13299             :                                    int bApproxOK, double *pdfMin,
   13300             :                                    double *pdfMax, double *pdfMean,
   13301             :                                    double *pdfStdDev, GUInt64 *pnValidCount,
   13302             :                                    GDALProgressFunc pfnProgress,
   13303             :                                    void *pProgressData,
   13304             :                                    CSLConstList papszOptions)
   13305             : {
   13306           4 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13307           8 :     return hArray->m_poImpl->ComputeStatistics(
   13308           4 :         CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
   13309           8 :         pnValidCount, pfnProgress, pProgressData, papszOptions);
   13310             : }
   13311             : 
   13312             : /************************************************************************/
   13313             : /*                 GDALMDArrayGetCoordinateVariables()                  */
   13314             : /************************************************************************/
   13315             : 
   13316             : /** Return coordinate variables.
   13317             :  *
   13318             :  * The returned array must be freed with GDALReleaseArrays(). If only the array
   13319             :  * itself needs to be freed, CPLFree() should be called (and
   13320             :  * GDALMDArrayRelease() on individual array members).
   13321             :  *
   13322             :  * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
   13323             :  *
   13324             :  * @param hArray Array.
   13325             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13326             :  *
   13327             :  * @return an array of *pnCount arrays.
   13328             :  * @since 3.4
   13329             :  */
   13330          13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
   13331             :                                                 size_t *pnCount)
   13332             : {
   13333          13 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13334          13 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13335          13 :     const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
   13336             :     auto ret = static_cast<GDALMDArrayH *>(
   13337          13 :         CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
   13338          29 :     for (size_t i = 0; i < coordinates.size(); i++)
   13339             :     {
   13340          16 :         ret[i] = new GDALMDArrayHS(coordinates[i]);
   13341             :     }
   13342          13 :     *pnCount = coordinates.size();
   13343          13 :     return ret;
   13344             : }
   13345             : 
   13346             : /************************************************************************/
   13347             : /*                     GDALMDArrayGetGridded()                          */
   13348             : /************************************************************************/
   13349             : 
   13350             : /** Return a gridded array from scattered point data, that is from an array
   13351             :  * whose last dimension is the indexing variable of X and Y arrays.
   13352             :  *
   13353             :  * The returned object should be released with GDALMDArrayRelease().
   13354             :  *
   13355             :  * This is the same as the C++ method GDALMDArray::GetGridded().
   13356             :  *
   13357             :  * @since GDAL 3.7
   13358             :  */
   13359          22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
   13360             :                                    const char *pszGridOptions,
   13361             :                                    GDALMDArrayH hXArray, GDALMDArrayH hYArray,
   13362             :                                    CSLConstList papszOptions)
   13363             : {
   13364          22 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13365          22 :     VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
   13366          22 :     auto gridded = hArray->m_poImpl->GetGridded(
   13367          44 :         pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
   13368          88 :         hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
   13369          22 :     if (!gridded)
   13370          19 :         return nullptr;
   13371           3 :     return new GDALMDArrayHS(gridded);
   13372             : }
   13373             : 
   13374             : /************************************************************************/
   13375             : /*                      GDALMDArrayGetMeshGrid()                        */
   13376             : /************************************************************************/
   13377             : 
   13378             : /** Return a list of multidimensional arrays from a list of one-dimensional
   13379             :  * arrays.
   13380             :  *
   13381             :  * This is typically used to transform one-dimensional longitude, latitude
   13382             :  * arrays into 2D ones.
   13383             :  *
   13384             :  * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
   13385             :  * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
   13386             :  * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
   13387             :  * repeated to fill the matrix along the first dimension for x1, the second
   13388             :  * for x2 and so on.
   13389             :  *
   13390             :  * For example, if x = [1, 2], and y = [3, 4, 5],
   13391             :  * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
   13392             :  * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
   13393             :  * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
   13394             :  *
   13395             :  * and
   13396             :  * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
   13397             :  * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
   13398             :  * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
   13399             :  *
   13400             :  * The currently supported options are:
   13401             :  * <ul>
   13402             :  * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
   13403             :  * output.
   13404             :  * </li>
   13405             :  * </ul>
   13406             :  *
   13407             :  * This is the same as
   13408             :  * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
   13409             :  * function.
   13410             :  *
   13411             :  * The returned array (of arrays) must be freed with GDALReleaseArrays().
   13412             :  * If only the array itself needs to be freed, CPLFree() should be called
   13413             :  * (and GDALMDArrayRelease() on individual array members).
   13414             :  *
   13415             :  * This is the same as the C++ method GDALMDArray::GetMeshGrid()
   13416             :  *
   13417             :  * @param pahInputArrays Input arrays
   13418             :  * @param nCountInputArrays Number of input arrays
   13419             :  * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
   13420             :  * @param papszOptions NULL, or NULL terminated list of options.
   13421             :  *
   13422             :  * @return an array of *pnCountOutputArrays arrays.
   13423             :  * @since 3.10
   13424             :  */
   13425           7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
   13426             :                                      size_t nCountInputArrays,
   13427             :                                      size_t *pnCountOutputArrays,
   13428             :                                      CSLConstList papszOptions)
   13429             : {
   13430           7 :     VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
   13431           7 :     VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
   13432             : 
   13433          14 :     std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
   13434          20 :     for (size_t i = 0; i < nCountInputArrays; ++i)
   13435          13 :         apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
   13436             : 
   13437             :     const auto apoOutputArrays =
   13438           7 :         GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
   13439             :     auto ret = static_cast<GDALMDArrayH *>(
   13440           7 :         CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
   13441          17 :     for (size_t i = 0; i < apoOutputArrays.size(); i++)
   13442             :     {
   13443          10 :         ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
   13444             :     }
   13445           7 :     *pnCountOutputArrays = apoOutputArrays.size();
   13446           7 :     return ret;
   13447             : }
   13448             : 
   13449             : /************************************************************************/
   13450             : /*                        GDALReleaseArrays()                           */
   13451             : /************************************************************************/
   13452             : 
   13453             : /** Free the return of GDALMDArrayGetCoordinateVariables()
   13454             :  *
   13455             :  * @param arrays return pointer of above methods
   13456             :  * @param nCount *pnCount value returned by above methods
   13457             :  */
   13458          20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
   13459             : {
   13460          46 :     for (size_t i = 0; i < nCount; i++)
   13461             :     {
   13462          26 :         delete arrays[i];
   13463             :     }
   13464          20 :     CPLFree(arrays);
   13465          20 : }
   13466             : 
   13467             : /************************************************************************/
   13468             : /*                           GDALMDArrayCache()                         */
   13469             : /************************************************************************/
   13470             : 
   13471             : /**
   13472             :  * \brief Cache the content of the array into an auxiliary filename.
   13473             :  *
   13474             :  * This is the same as the C++ method GDALMDArray::Cache().
   13475             :  *
   13476             :  * @since GDAL 3.4
   13477             :  */
   13478             : 
   13479           7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
   13480             : {
   13481           7 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13482           7 :     return hArray->m_poImpl->Cache(papszOptions);
   13483             : }
   13484             : 
   13485             : /************************************************************************/
   13486             : /*                       GDALMDArrayRename()                           */
   13487             : /************************************************************************/
   13488             : 
   13489             : /** Rename the array.
   13490             :  *
   13491             :  * This is not implemented by all drivers.
   13492             :  *
   13493             :  * Drivers known to implement it: MEM, netCDF, Zarr.
   13494             :  *
   13495             :  * This is the same as the C++ method GDALAbstractMDArray::Rename()
   13496             :  *
   13497             :  * @return true in case of success
   13498             :  * @since GDAL 3.8
   13499             :  */
   13500          28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
   13501             : {
   13502          28 :     VALIDATE_POINTER1(hArray, __func__, false);
   13503          28 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   13504          28 :     return hArray->m_poImpl->Rename(pszNewName);
   13505             : }
   13506             : 
   13507             : /************************************************************************/
   13508             : /*                        GDALAttributeRelease()                        */
   13509             : /************************************************************************/
   13510             : 
   13511             : /** Release the GDAL in-memory object associated with a GDALAttribute.
   13512             :  *
   13513             :  * Note: when applied on a object coming from a driver, this does not
   13514             :  * destroy the object in the file, database, etc...
   13515             :  */
   13516         765 : void GDALAttributeRelease(GDALAttributeH hAttr)
   13517             : {
   13518         765 :     delete hAttr;
   13519         765 : }
   13520             : 
   13521             : /************************************************************************/
   13522             : /*                        GDALAttributeGetName()                        */
   13523             : /************************************************************************/
   13524             : 
   13525             : /** Return the name of the attribute.
   13526             :  *
   13527             :  * The returned pointer is valid until hAttr is released.
   13528             :  *
   13529             :  * This is the same as the C++ method GDALAttribute::GetName().
   13530             :  */
   13531         361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
   13532             : {
   13533         361 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13534         361 :     return hAttr->m_poImpl->GetName().c_str();
   13535             : }
   13536             : 
   13537             : /************************************************************************/
   13538             : /*                      GDALAttributeGetFullName()                      */
   13539             : /************************************************************************/
   13540             : 
   13541             : /** Return the full name of the attribute.
   13542             :  *
   13543             :  * The returned pointer is valid until hAttr is released.
   13544             :  *
   13545             :  * This is the same as the C++ method GDALAttribute::GetFullName().
   13546             :  */
   13547          49 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
   13548             : {
   13549          49 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13550          49 :     return hAttr->m_poImpl->GetFullName().c_str();
   13551             : }
   13552             : 
   13553             : /************************************************************************/
   13554             : /*                   GDALAttributeGetTotalElementsCount()               */
   13555             : /************************************************************************/
   13556             : 
   13557             : /** Return the total number of values in the attribute.
   13558             :  *
   13559             :  * This is the same as the C++ method
   13560             :  * GDALAbstractMDArray::GetTotalElementsCount()
   13561             :  */
   13562         177 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
   13563             : {
   13564         177 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13565         177 :     return hAttr->m_poImpl->GetTotalElementsCount();
   13566             : }
   13567             : 
   13568             : /************************************************************************/
   13569             : /*                    GDALAttributeGetDimensionCount()                */
   13570             : /************************************************************************/
   13571             : 
   13572             : /** Return the number of dimensions.
   13573             :  *
   13574             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
   13575             :  */
   13576          12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
   13577             : {
   13578          12 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13579          12 :     return hAttr->m_poImpl->GetDimensionCount();
   13580             : }
   13581             : 
   13582             : /************************************************************************/
   13583             : /*                       GDALAttributeGetDimensionsSize()                */
   13584             : /************************************************************************/
   13585             : 
   13586             : /** Return the dimension sizes of the attribute.
   13587             :  *
   13588             :  * The returned array must be freed with CPLFree()
   13589             :  *
   13590             :  * @param hAttr Attribute.
   13591             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13592             :  *
   13593             :  * @return an array of *pnCount values.
   13594             :  */
   13595          11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
   13596             : {
   13597          11 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13598          11 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13599          11 :     const auto &dims = hAttr->m_poImpl->GetDimensions();
   13600          11 :     auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
   13601          22 :     for (size_t i = 0; i < dims.size(); i++)
   13602             :     {
   13603          11 :         ret[i] = dims[i]->GetSize();
   13604             :     }
   13605          11 :     *pnCount = dims.size();
   13606          11 :     return ret;
   13607             : }
   13608             : 
   13609             : /************************************************************************/
   13610             : /*                       GDALAttributeGetDataType()                     */
   13611             : /************************************************************************/
   13612             : 
   13613             : /** Return the data type
   13614             :  *
   13615             :  * The return must be freed with GDALExtendedDataTypeRelease().
   13616             :  */
   13617         434 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
   13618             : {
   13619         434 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13620             :     return new GDALExtendedDataTypeHS(
   13621         434 :         new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
   13622             : }
   13623             : 
   13624             : /************************************************************************/
   13625             : /*                       GDALAttributeReadAsRaw()                       */
   13626             : /************************************************************************/
   13627             : 
   13628             : /** Return the raw value of an attribute.
   13629             :  *
   13630             :  * This is the same as the C++ method GDALAttribute::ReadAsRaw().
   13631             :  *
   13632             :  * The returned buffer must be freed with GDALAttributeFreeRawResult()
   13633             :  *
   13634             :  * @param hAttr Attribute.
   13635             :  * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
   13636             :  *
   13637             :  * @return a buffer of *pnSize bytes.
   13638             :  */
   13639           6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
   13640             : {
   13641           6 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13642           6 :     VALIDATE_POINTER1(pnSize, __func__, nullptr);
   13643          12 :     auto res(hAttr->m_poImpl->ReadAsRaw());
   13644           6 :     *pnSize = res.size();
   13645           6 :     auto ret = res.StealData();
   13646           6 :     if (!ret)
   13647             :     {
   13648           0 :         *pnSize = 0;
   13649           0 :         return nullptr;
   13650             :     }
   13651           6 :     return ret;
   13652             : }
   13653             : 
   13654             : /************************************************************************/
   13655             : /*                       GDALAttributeFreeRawResult()                   */
   13656             : /************************************************************************/
   13657             : 
   13658             : /** Free the return of GDALAttributeAsRaw()
   13659             :  */
   13660           6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
   13661             :                                 CPL_UNUSED size_t nSize)
   13662             : {
   13663           6 :     VALIDATE_POINTER0(hAttr, __func__);
   13664           6 :     if (raw)
   13665             :     {
   13666           6 :         const auto &dt(hAttr->m_poImpl->GetDataType());
   13667           6 :         const auto nDTSize(dt.GetSize());
   13668           6 :         GByte *pabyPtr = raw;
   13669           6 :         const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
   13670           6 :         CPLAssert(nSize == nDTSize * nEltCount);
   13671          12 :         for (size_t i = 0; i < nEltCount; ++i)
   13672             :         {
   13673           6 :             dt.FreeDynamicMemory(pabyPtr);
   13674           6 :             pabyPtr += nDTSize;
   13675             :         }
   13676           6 :         CPLFree(raw);
   13677             :     }
   13678             : }
   13679             : 
   13680             : /************************************************************************/
   13681             : /*                       GDALAttributeReadAsString()                    */
   13682             : /************************************************************************/
   13683             : 
   13684             : /** Return the value of an attribute as a string.
   13685             :  *
   13686             :  * The returned string should not be freed, and its lifetime does not
   13687             :  * excess a next call to ReadAsString() on the same object, or the deletion
   13688             :  * of the object itself.
   13689             :  *
   13690             :  * This function will only return the first element if there are several.
   13691             :  *
   13692             :  * This is the same as the C++ method GDALAttribute::ReadAsString()
   13693             :  *
   13694             :  * @return a string, or nullptr.
   13695             :  */
   13696         108 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
   13697             : {
   13698         108 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13699         108 :     return hAttr->m_poImpl->ReadAsString();
   13700             : }
   13701             : 
   13702             : /************************************************************************/
   13703             : /*                      GDALAttributeReadAsInt()                        */
   13704             : /************************************************************************/
   13705             : 
   13706             : /** Return the value of an attribute as a integer.
   13707             :  *
   13708             :  * This function will only return the first element if there are several.
   13709             :  *
   13710             :  * It can fail if its value can not be converted to integer.
   13711             :  *
   13712             :  * This is the same as the C++ method GDALAttribute::ReadAsInt()
   13713             :  *
   13714             :  * @return a integer, or INT_MIN in case of error.
   13715             :  */
   13716          22 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
   13717             : {
   13718          22 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13719          22 :     return hAttr->m_poImpl->ReadAsInt();
   13720             : }
   13721             : 
   13722             : /************************************************************************/
   13723             : /*                      GDALAttributeReadAsInt64()                      */
   13724             : /************************************************************************/
   13725             : 
   13726             : /** Return the value of an attribute as a int64_t.
   13727             :  *
   13728             :  * This function will only return the first element if there are several.
   13729             :  *
   13730             :  * It can fail if its value can not be converted to integer.
   13731             :  *
   13732             :  * This is the same as the C++ method GDALAttribute::ReadAsInt64()
   13733             :  *
   13734             :  * @return an int64_t, or INT64_MIN in case of error.
   13735             :  */
   13736          15 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
   13737             : {
   13738          15 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13739          15 :     return hAttr->m_poImpl->ReadAsInt64();
   13740             : }
   13741             : 
   13742             : /************************************************************************/
   13743             : /*                       GDALAttributeReadAsDouble()                    */
   13744             : /************************************************************************/
   13745             : 
   13746             : /** Return the value of an attribute as a double.
   13747             :  *
   13748             :  * This function will only return the first element if there are several.
   13749             :  *
   13750             :  * It can fail if its value can not be converted to double.
   13751             :  *
   13752             :  * This is the same as the C++ method GDALAttribute::ReadAsDouble()
   13753             :  *
   13754             :  * @return a double value.
   13755             :  */
   13756          40 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
   13757             : {
   13758          40 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13759          40 :     return hAttr->m_poImpl->ReadAsDouble();
   13760             : }
   13761             : 
   13762             : /************************************************************************/
   13763             : /*                     GDALAttributeReadAsStringArray()                 */
   13764             : /************************************************************************/
   13765             : 
   13766             : /** Return the value of an attribute as an array of strings.
   13767             :  *
   13768             :  * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
   13769             :  *
   13770             :  * The return value must be freed with CSLDestroy().
   13771             :  */
   13772          19 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
   13773             : {
   13774          19 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13775          19 :     return hAttr->m_poImpl->ReadAsStringArray().StealList();
   13776             : }
   13777             : 
   13778             : /************************************************************************/
   13779             : /*                     GDALAttributeReadAsIntArray()                    */
   13780             : /************************************************************************/
   13781             : 
   13782             : /** Return the value of an attribute as an array of integers.
   13783             :  *
   13784             :  * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
   13785             :  *
   13786             :  * @param hAttr Attribute
   13787             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13788             :  * @return array to be freed with CPLFree(), or nullptr.
   13789             :  */
   13790          15 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
   13791             : {
   13792          15 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13793          15 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13794          15 :     *pnCount = 0;
   13795          30 :     auto tmp(hAttr->m_poImpl->ReadAsIntArray());
   13796          15 :     if (tmp.empty())
   13797           0 :         return nullptr;
   13798          15 :     auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
   13799          15 :     if (!ret)
   13800           0 :         return nullptr;
   13801          15 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
   13802          15 :     *pnCount = tmp.size();
   13803          15 :     return ret;
   13804             : }
   13805             : 
   13806             : /************************************************************************/
   13807             : /*                     GDALAttributeReadAsInt64Array()                  */
   13808             : /************************************************************************/
   13809             : 
   13810             : /** Return the value of an attribute as an array of int64_t.
   13811             :  *
   13812             :  * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
   13813             :  *
   13814             :  * @param hAttr Attribute
   13815             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13816             :  * @return array to be freed with CPLFree(), or nullptr.
   13817             :  */
   13818          14 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
   13819             : {
   13820          14 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13821          14 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13822          14 :     *pnCount = 0;
   13823          28 :     auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
   13824          14 :     if (tmp.empty())
   13825           0 :         return nullptr;
   13826             :     auto ret = static_cast<int64_t *>(
   13827          14 :         VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
   13828          14 :     if (!ret)
   13829           0 :         return nullptr;
   13830          14 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
   13831          14 :     *pnCount = tmp.size();
   13832          14 :     return ret;
   13833             : }
   13834             : 
   13835             : /************************************************************************/
   13836             : /*                     GDALAttributeReadAsDoubleArray()                 */
   13837             : /************************************************************************/
   13838             : 
   13839             : /** Return the value of an attribute as an array of doubles.
   13840             :  *
   13841             :  * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
   13842             :  *
   13843             :  * @param hAttr Attribute
   13844             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13845             :  * @return array to be freed with CPLFree(), or nullptr.
   13846             :  */
   13847          29 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
   13848             : {
   13849          29 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13850          29 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13851          29 :     *pnCount = 0;
   13852          58 :     auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
   13853          29 :     if (tmp.empty())
   13854           0 :         return nullptr;
   13855             :     auto ret =
   13856          29 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
   13857          29 :     if (!ret)
   13858           0 :         return nullptr;
   13859          29 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
   13860          29 :     *pnCount = tmp.size();
   13861          29 :     return ret;
   13862             : }
   13863             : 
   13864             : /************************************************************************/
   13865             : /*                     GDALAttributeWriteRaw()                          */
   13866             : /************************************************************************/
   13867             : 
   13868             : /** Write an attribute from raw values expressed in GetDataType()
   13869             :  *
   13870             :  * The values should be provided in the type of GetDataType() and there should
   13871             :  * be exactly GetTotalElementsCount() of them.
   13872             :  * If GetDataType() is a string, each value should be a char* pointer.
   13873             :  *
   13874             :  * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
   13875             :  *
   13876             :  * @param hAttr Attribute
   13877             :  * @param pabyValue Buffer of nLen bytes.
   13878             :  * @param nLength Size of pabyValue in bytes. Should be equal to
   13879             :  *             GetTotalElementsCount() * GetDataType().GetSize()
   13880             :  * @return TRUE in case of success.
   13881             :  */
   13882           5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
   13883             :                           size_t nLength)
   13884             : {
   13885           5 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13886           5 :     return hAttr->m_poImpl->Write(pabyValue, nLength);
   13887             : }
   13888             : 
   13889             : /************************************************************************/
   13890             : /*                     GDALAttributeWriteString()                       */
   13891             : /************************************************************************/
   13892             : 
   13893             : /** Write an attribute from a string value.
   13894             :  *
   13895             :  * Type conversion will be performed if needed. If the attribute contains
   13896             :  * multiple values, only the first one will be updated.
   13897             :  *
   13898             :  * This is the same as the C++ method GDALAttribute::Write(const char*)
   13899             :  *
   13900             :  * @param hAttr Attribute
   13901             :  * @param pszVal Pointer to a string.
   13902             :  * @return TRUE in case of success.
   13903             :  */
   13904         207 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
   13905             : {
   13906         207 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13907         207 :     return hAttr->m_poImpl->Write(pszVal);
   13908             : }
   13909             : 
   13910             : /************************************************************************/
   13911             : /*                        GDALAttributeWriteInt()                       */
   13912             : /************************************************************************/
   13913             : 
   13914             : /** Write an attribute from a integer value.
   13915             :  *
   13916             :  * Type conversion will be performed if needed. If the attribute contains
   13917             :  * multiple values, only the first one will be updated.
   13918             :  *
   13919             :  * This is the same as the C++ method GDALAttribute::WriteInt()
   13920             :  *
   13921             :  * @param hAttr Attribute
   13922             :  * @param nVal Value.
   13923             :  * @return TRUE in case of success.
   13924             :  */
   13925          22 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
   13926             : {
   13927          22 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13928          22 :     return hAttr->m_poImpl->WriteInt(nVal);
   13929             : }
   13930             : 
   13931             : /************************************************************************/
   13932             : /*                        GDALAttributeWriteInt64()                     */
   13933             : /************************************************************************/
   13934             : 
   13935             : /** Write an attribute from an int64_t value.
   13936             :  *
   13937             :  * Type conversion will be performed if needed. If the attribute contains
   13938             :  * multiple values, only the first one will be updated.
   13939             :  *
   13940             :  * This is the same as the C++ method GDALAttribute::WriteLong()
   13941             :  *
   13942             :  * @param hAttr Attribute
   13943             :  * @param nVal Value.
   13944             :  * @return TRUE in case of success.
   13945             :  */
   13946          11 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
   13947             : {
   13948          11 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13949          11 :     return hAttr->m_poImpl->WriteInt64(nVal);
   13950             : }
   13951             : 
   13952             : /************************************************************************/
   13953             : /*                        GDALAttributeWriteDouble()                    */
   13954             : /************************************************************************/
   13955             : 
   13956             : /** Write an attribute from a double value.
   13957             :  *
   13958             :  * Type conversion will be performed if needed. If the attribute contains
   13959             :  * multiple values, only the first one will be updated.
   13960             :  *
   13961             :  * This is the same as the C++ method GDALAttribute::Write(double);
   13962             :  *
   13963             :  * @param hAttr Attribute
   13964             :  * @param dfVal Value.
   13965             :  *
   13966             :  * @return TRUE in case of success.
   13967             :  */
   13968          11 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
   13969             : {
   13970          11 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13971          11 :     return hAttr->m_poImpl->Write(dfVal);
   13972             : }
   13973             : 
   13974             : /************************************************************************/
   13975             : /*                       GDALAttributeWriteStringArray()                */
   13976             : /************************************************************************/
   13977             : 
   13978             : /** Write an attribute from an array of strings.
   13979             :  *
   13980             :  * Type conversion will be performed if needed.
   13981             :  *
   13982             :  * Exactly GetTotalElementsCount() strings must be provided
   13983             :  *
   13984             :  * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
   13985             :  *
   13986             :  * @param hAttr Attribute
   13987             :  * @param papszValues Array of strings.
   13988             :  * @return TRUE in case of success.
   13989             :  */
   13990           8 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
   13991             :                                   CSLConstList papszValues)
   13992             : {
   13993           8 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13994           8 :     return hAttr->m_poImpl->Write(papszValues);
   13995             : }
   13996             : 
   13997             : /************************************************************************/
   13998             : /*                       GDALAttributeWriteIntArray()                */
   13999             : /************************************************************************/
   14000             : 
   14001             : /** Write an attribute from an array of int.
   14002             :  *
   14003             :  * Type conversion will be performed if needed.
   14004             :  *
   14005             :  * Exactly GetTotalElementsCount() strings must be provided
   14006             :  *
   14007             :  * This is the same as the C++ method GDALAttribute::Write(const int *,
   14008             :  * size_t)
   14009             :  *
   14010             :  * @param hAttr Attribute
   14011             :  * @param panValues Array of int.
   14012             :  * @param nCount Should be equal to GetTotalElementsCount().
   14013             :  * @return TRUE in case of success.
   14014             :  */
   14015          11 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
   14016             :                                size_t nCount)
   14017             : {
   14018          11 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   14019          11 :     return hAttr->m_poImpl->Write(panValues, nCount);
   14020             : }
   14021             : 
   14022             : /************************************************************************/
   14023             : /*                       GDALAttributeWriteInt64Array()                 */
   14024             : /************************************************************************/
   14025             : 
   14026             : /** Write an attribute from an array of int64_t.
   14027             :  *
   14028             :  * Type conversion will be performed if needed.
   14029             :  *
   14030             :  * Exactly GetTotalElementsCount() strings must be provided
   14031             :  *
   14032             :  * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
   14033             :  * size_t)
   14034             :  *
   14035             :  * @param hAttr Attribute
   14036             :  * @param panValues Array of int64_t.
   14037             :  * @param nCount Should be equal to GetTotalElementsCount().
   14038             :  * @return TRUE in case of success.
   14039             :  */
   14040          10 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
   14041             :                                  size_t nCount)
   14042             : {
   14043          10 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   14044          10 :     return hAttr->m_poImpl->Write(panValues, nCount);
   14045             : }
   14046             : 
   14047             : /************************************************************************/
   14048             : /*                       GDALAttributeWriteDoubleArray()                */
   14049             : /************************************************************************/
   14050             : 
   14051             : /** Write an attribute from an array of double.
   14052             :  *
   14053             :  * Type conversion will be performed if needed.
   14054             :  *
   14055             :  * Exactly GetTotalElementsCount() strings must be provided
   14056             :  *
   14057             :  * This is the same as the C++ method GDALAttribute::Write(const double *,
   14058             :  * size_t)
   14059             :  *
   14060             :  * @param hAttr Attribute
   14061             :  * @param padfValues Array of double.
   14062             :  * @param nCount Should be equal to GetTotalElementsCount().
   14063             :  * @return TRUE in case of success.
   14064             :  */
   14065           7 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
   14066             :                                   const double *padfValues, size_t nCount)
   14067             : {
   14068           7 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   14069           7 :     return hAttr->m_poImpl->Write(padfValues, nCount);
   14070             : }
   14071             : 
   14072             : /************************************************************************/
   14073             : /*                      GDALAttributeRename()                           */
   14074             : /************************************************************************/
   14075             : 
   14076             : /** Rename the attribute.
   14077             :  *
   14078             :  * This is not implemented by all drivers.
   14079             :  *
   14080             :  * Drivers known to implement it: MEM, netCDF.
   14081             :  *
   14082             :  * This is the same as the C++ method GDALAbstractMDArray::Rename()
   14083             :  *
   14084             :  * @return true in case of success
   14085             :  * @since GDAL 3.8
   14086             :  */
   14087          27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
   14088             : {
   14089          27 :     VALIDATE_POINTER1(hAttr, __func__, false);
   14090          27 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   14091          27 :     return hAttr->m_poImpl->Rename(pszNewName);
   14092             : }
   14093             : 
   14094             : /************************************************************************/
   14095             : /*                        GDALDimensionRelease()                        */
   14096             : /************************************************************************/
   14097             : 
   14098             : /** Release the GDAL in-memory object associated with a GDALDimension.
   14099             :  *
   14100             :  * Note: when applied on a object coming from a driver, this does not
   14101             :  * destroy the object in the file, database, etc...
   14102             :  */
   14103        5324 : void GDALDimensionRelease(GDALDimensionH hDim)
   14104             : {
   14105        5324 :     delete hDim;
   14106        5324 : }
   14107             : 
   14108             : /************************************************************************/
   14109             : /*                        GDALDimensionGetName()                        */
   14110             : /************************************************************************/
   14111             : 
   14112             : /** Return dimension name.
   14113             :  *
   14114             :  * This is the same as the C++ method GDALDimension::GetName()
   14115             :  */
   14116         296 : const char *GDALDimensionGetName(GDALDimensionH hDim)
   14117             : {
   14118         296 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14119         296 :     return hDim->m_poImpl->GetName().c_str();
   14120             : }
   14121             : 
   14122             : /************************************************************************/
   14123             : /*                      GDALDimensionGetFullName()                      */
   14124             : /************************************************************************/
   14125             : 
   14126             : /** Return dimension full name.
   14127             :  *
   14128             :  * This is the same as the C++ method GDALDimension::GetFullName()
   14129             :  */
   14130          82 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
   14131             : {
   14132          82 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14133          82 :     return hDim->m_poImpl->GetFullName().c_str();
   14134             : }
   14135             : 
   14136             : /************************************************************************/
   14137             : /*                        GDALDimensionGetType()                        */
   14138             : /************************************************************************/
   14139             : 
   14140             : /** Return dimension type.
   14141             :  *
   14142             :  * This is the same as the C++ method GDALDimension::GetType()
   14143             :  */
   14144          70 : const char *GDALDimensionGetType(GDALDimensionH hDim)
   14145             : {
   14146          70 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14147          70 :     return hDim->m_poImpl->GetType().c_str();
   14148             : }
   14149             : 
   14150             : /************************************************************************/
   14151             : /*                     GDALDimensionGetDirection()                      */
   14152             : /************************************************************************/
   14153             : 
   14154             : /** Return dimension direction.
   14155             :  *
   14156             :  * This is the same as the C++ method GDALDimension::GetDirection()
   14157             :  */
   14158          38 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
   14159             : {
   14160          38 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14161          38 :     return hDim->m_poImpl->GetDirection().c_str();
   14162             : }
   14163             : 
   14164             : /************************************************************************/
   14165             : /*                        GDALDimensionGetSize()                        */
   14166             : /************************************************************************/
   14167             : 
   14168             : /** Return the size, that is the number of values along the dimension.
   14169             :  *
   14170             :  * This is the same as the C++ method GDALDimension::GetSize()
   14171             :  */
   14172        3962 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
   14173             : {
   14174        3962 :     VALIDATE_POINTER1(hDim, __func__, 0);
   14175        3962 :     return hDim->m_poImpl->GetSize();
   14176             : }
   14177             : 
   14178             : /************************************************************************/
   14179             : /*                     GDALDimensionGetIndexingVariable()               */
   14180             : /************************************************************************/
   14181             : 
   14182             : /** Return the variable that is used to index the dimension (if there is one).
   14183             :  *
   14184             :  * This is the array, typically one-dimensional, describing the values taken
   14185             :  * by the dimension.
   14186             :  *
   14187             :  * The returned value should be freed with GDALMDArrayRelease().
   14188             :  *
   14189             :  * This is the same as the C++ method GDALDimension::GetIndexingVariable()
   14190             :  */
   14191         140 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
   14192             : {
   14193         140 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14194         280 :     auto var(hDim->m_poImpl->GetIndexingVariable());
   14195         140 :     if (!var)
   14196          11 :         return nullptr;
   14197         129 :     return new GDALMDArrayHS(var);
   14198             : }
   14199             : 
   14200             : /************************************************************************/
   14201             : /*                      GDALDimensionSetIndexingVariable()              */
   14202             : /************************************************************************/
   14203             : 
   14204             : /** Set the variable that is used to index the dimension.
   14205             :  *
   14206             :  * This is the array, typically one-dimensional, describing the values taken
   14207             :  * by the dimension.
   14208             :  *
   14209             :  * This is the same as the C++ method GDALDimension::SetIndexingVariable()
   14210             :  *
   14211             :  * @return TRUE in case of success.
   14212             :  */
   14213          23 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
   14214             : {
   14215          23 :     VALIDATE_POINTER1(hDim, __func__, FALSE);
   14216          69 :     return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
   14217          46 :                                                       : nullptr);
   14218             : }
   14219             : 
   14220             : /************************************************************************/
   14221             : /*                      GDALDimensionRename()                           */
   14222             : /************************************************************************/
   14223             : 
   14224             : /** Rename the dimension.
   14225             :  *
   14226             :  * This is not implemented by all drivers.
   14227             :  *
   14228             :  * Drivers known to implement it: MEM, netCDF.
   14229             :  *
   14230             :  * This is the same as the C++ method GDALDimension::Rename()
   14231             :  *
   14232             :  * @return true in case of success
   14233             :  * @since GDAL 3.8
   14234             :  */
   14235          31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
   14236             : {
   14237          31 :     VALIDATE_POINTER1(hDim, __func__, false);
   14238          31 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   14239          31 :     return hDim->m_poImpl->Rename(pszNewName);
   14240             : }
   14241             : 
   14242             : /************************************************************************/
   14243             : /*                       GDALDatasetGetRootGroup()                      */
   14244             : /************************************************************************/
   14245             : 
   14246             : /** Return the root GDALGroup of this dataset.
   14247             :  *
   14248             :  * Only valid for multidimensional datasets.
   14249             :  *
   14250             :  * The returned value must be freed with GDALGroupRelease().
   14251             :  *
   14252             :  * This is the same as the C++ method GDALDataset::GetRootGroup().
   14253             :  *
   14254             :  * @since GDAL 3.1
   14255             :  */
   14256        1269 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
   14257             : {
   14258        1269 :     VALIDATE_POINTER1(hDS, __func__, nullptr);
   14259        1269 :     auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
   14260        1269 :     return poGroup ? new GDALGroupHS(poGroup) : nullptr;
   14261             : }
   14262             : 
   14263             : /************************************************************************/
   14264             : /*                      GDALRasterBandAsMDArray()                        */
   14265             : /************************************************************************/
   14266             : 
   14267             : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
   14268             :  *
   14269             :  * The band must be linked to a GDALDataset. If this dataset is not already
   14270             :  * marked as shared, it will be, so that the returned array holds a reference
   14271             :  * to it.
   14272             :  *
   14273             :  * If the dataset has a geotransform attached, the X and Y dimensions of the
   14274             :  * returned array will have an associated indexing variable.
   14275             :  *
   14276             :  * The returned pointer must be released with GDALMDArrayRelease().
   14277             :  *
   14278             :  * This is the same as the C++ method GDALRasterBand::AsMDArray().
   14279             :  *
   14280             :  * @return a new array, or NULL.
   14281             :  *
   14282             :  * @since GDAL 3.1
   14283             :  */
   14284          24 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
   14285             : {
   14286          24 :     VALIDATE_POINTER1(hBand, __func__, nullptr);
   14287          48 :     auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
   14288          24 :     if (!poArray)
   14289           0 :         return nullptr;
   14290          24 :     return new GDALMDArrayHS(poArray);
   14291             : }
   14292             : 
   14293             : /************************************************************************/
   14294             : /*                        GDALDatasetAsMDArray()                        */
   14295             : /************************************************************************/
   14296             : 
   14297             : /** Return a view of this dataset as a 3D multidimensional GDALMDArray.
   14298             :  *
   14299             :  * If this dataset is not already marked as shared, it will be, so that the
   14300             :  * returned array holds a reference to it.
   14301             :  *
   14302             :  * If the dataset has a geotransform attached, the X and Y dimensions of the
   14303             :  * returned array will have an associated indexing variable.
   14304             :  *
   14305             :  * The currently supported list of options is:
   14306             :  * <ul>
   14307             :  * <li>DIM_ORDER=&lt;order&gt; where order can be "AUTO", "Band,Y,X" or "Y,X,Band".
   14308             :  * "Band,Y,X" means that the first (slowest changing) dimension is Band
   14309             :  * and the last (fastest changing direction) is X
   14310             :  * "Y,X,Band" means that the first (slowest changing) dimension is Y
   14311             :  * and the last (fastest changing direction) is Band.
   14312             :  * "AUTO" (the default) selects "Band,Y,X" for single band datasets, or takes
   14313             :  * into account the INTERLEAVE metadata item in the IMAGE_STRUCTURE domain.
   14314             :  * If it equals BAND, then "Band,Y,X" is used. Otherwise (if it equals PIXEL),
   14315             :  * "Y,X,Band" is use.
   14316             :  * </li>
   14317             :  * <li>BAND_INDEXING_VAR_ITEM={Description}|{None}|{Index}|{ColorInterpretation}|&lt;BandMetadataItem&gt;:
   14318             :  * item from which to build the band indexing variable.
   14319             :  * <ul>
   14320             :  * <li>"{Description}", the default, means to use the band description (or "Band index" if empty).</li>
   14321             :  * <li>"{None}" means that no band indexing variable must be created.</li>
   14322             :  * <li>"{Index}" means that the band index (starting at one) is used.</li>
   14323             :  * <li>"{ColorInterpretation}" means that the band color interpretation is used (i.e. "Red", "Green", "Blue").</li>
   14324             :  * <li>&lt;BandMetadataItem&gt; is the name of a band metadata item to use.</li>
   14325             :  * </ul>
   14326             :  * </li>
   14327             :  * <li>BAND_INDEXING_VAR_TYPE=String|Real|Integer: the data type of the band
   14328             :  * indexing variable, when BAND_INDEXING_VAR_ITEM corresponds to a band metadata item.
   14329             :  * Defaults to String.
   14330             :  * </li>
   14331             :  * <li>BAND_DIM_NAME=&lt;string&gt;: Name of the band dimension.
   14332             :  * Defaults to "Band".
   14333             :  * </li>
   14334             :  * <li>X_DIM_NAME=&lt;string&gt;: Name of the X dimension. Defaults to "X".
   14335             :  * </li>
   14336             :  * <li>Y_DIM_NAME=&lt;string&gt;: Name of the Y dimension. Defaults to "Y".
   14337             :  * </li>
   14338             :  * </ul>
   14339             :  *
   14340             :  * The returned pointer must be released with GDALMDArrayRelease().
   14341             :  *
   14342             :  * The "reverse" methods are GDALRasterBand::AsMDArray() and
   14343             :  * GDALDataset::AsMDArray()
   14344             :  *
   14345             :  * This is the same as the C++ method GDALDataset::AsMDArray().
   14346             :  *
   14347             :  * @param hDS Dataset handle.
   14348             :  * @param papszOptions Null-terminated list of strings, or nullptr.
   14349             :  * @return a new array, or NULL.
   14350             :  *
   14351             :  * @since GDAL 3.12
   14352             :  */
   14353          14 : GDALMDArrayH GDALDatasetAsMDArray(GDALDatasetH hDS, CSLConstList papszOptions)
   14354             : {
   14355          14 :     VALIDATE_POINTER1(hDS, __func__, nullptr);
   14356          28 :     auto poArray(GDALDataset::FromHandle(hDS)->AsMDArray(papszOptions));
   14357          14 :     if (!poArray)
   14358           3 :         return nullptr;
   14359          11 :     return new GDALMDArrayHS(poArray);
   14360             : }
   14361             : 
   14362             : /************************************************************************/
   14363             : /*                       GDALMDArrayAsClassicDataset()                  */
   14364             : /************************************************************************/
   14365             : 
   14366             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   14367             :  *
   14368             :  * Only 2D or more arrays are supported.
   14369             :  *
   14370             :  * In the case of > 2D arrays, additional dimensions will be represented as
   14371             :  * raster bands.
   14372             :  *
   14373             :  * The "reverse" methods are GDALRasterBand::AsMDArray() and
   14374             :  * GDALDataset::AsMDArray()
   14375             :  *
   14376             :  * This is the same as the C++ method GDALMDArray::AsClassicDataset().
   14377             :  *
   14378             :  * @param hArray Array.
   14379             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   14380             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   14381             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   14382             :  */
   14383           0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
   14384             :                                          size_t iYDim)
   14385             : {
   14386           0 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   14387           0 :     return GDALDataset::ToHandle(
   14388           0 :         hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
   14389             : }
   14390             : 
   14391             : /************************************************************************/
   14392             : /*                     GDALMDArrayAsClassicDatasetEx()                  */
   14393             : /************************************************************************/
   14394             : 
   14395             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   14396             :  *
   14397             :  * Only 2D or more arrays are supported.
   14398             :  *
   14399             :  * In the case of > 2D arrays, additional dimensions will be represented as
   14400             :  * raster bands.
   14401             :  *
   14402             :  * The "reverse" method is GDALRasterBand::AsMDArray().
   14403             :  *
   14404             :  * This is the same as the C++ method GDALMDArray::AsClassicDataset().
   14405             :  * @param hArray Array.
   14406             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   14407             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   14408             :  *              Ignored if the dimension count is 1.
   14409             :  * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
   14410             :  *                   BAND_IMAGERY_METADATA option.
   14411             :  * @param papszOptions Cf GDALMDArray::AsClassicDataset()
   14412             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   14413             :  * @since GDAL 3.8
   14414             :  */
   14415         102 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
   14416             :                                            size_t iYDim, GDALGroupH hRootGroup,
   14417             :                                            CSLConstList papszOptions)
   14418             : {
   14419         102 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   14420         204 :     return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
   14421         204 :         iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
   14422         204 :         papszOptions));
   14423             : }
   14424             : 
   14425             : //! @cond Doxygen_Suppress
   14426             : 
   14427         180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
   14428             :                                          const std::string &osName,
   14429             :                                          const std::string &osValue,
   14430         180 :                                          GDALExtendedDataTypeSubType eSubType)
   14431             :     : GDALAbstractMDArray(osParentName, osName),
   14432             :       GDALAttribute(osParentName, osName),
   14433         180 :       m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
   14434             : {
   14435         180 : }
   14436             : 
   14437             : const std::vector<std::shared_ptr<GDALDimension>> &
   14438          30 : GDALAttributeString::GetDimensions() const
   14439             : {
   14440          30 :     return m_dims;
   14441             : }
   14442             : 
   14443          21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
   14444             : {
   14445          21 :     return m_dt;
   14446             : }
   14447             : 
   14448          10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
   14449             :                                 const GPtrDiff_t *,
   14450             :                                 const GDALExtendedDataType &bufferDataType,
   14451             :                                 void *pDstBuffer) const
   14452             : {
   14453          10 :     if (bufferDataType.GetClass() != GEDTC_STRING)
   14454           0 :         return false;
   14455          10 :     char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
   14456          10 :     if (!pszStr)
   14457           0 :         return false;
   14458          10 :     memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
   14459          10 :     *static_cast<char **>(pDstBuffer) = pszStr;
   14460          10 :     return true;
   14461             : }
   14462             : 
   14463          66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   14464             :                                            const std::string &osName,
   14465          66 :                                            double dfValue)
   14466             :     : GDALAbstractMDArray(osParentName, osName),
   14467             :       GDALAttribute(osParentName, osName),
   14468          66 :       m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
   14469             : {
   14470          66 : }
   14471             : 
   14472          27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   14473             :                                            const std::string &osName,
   14474          27 :                                            int nValue)
   14475             :     : GDALAbstractMDArray(osParentName, osName),
   14476             :       GDALAttribute(osParentName, osName),
   14477          27 :       m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
   14478             : {
   14479          27 : }
   14480             : 
   14481           7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   14482             :                                            const std::string &osName,
   14483           7 :                                            const std::vector<GUInt32> &anValues)
   14484             :     : GDALAbstractMDArray(osParentName, osName),
   14485             :       GDALAttribute(osParentName, osName),
   14486           7 :       m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
   14487             : {
   14488           7 :     m_dims.push_back(std::make_shared<GDALDimension>(
   14489          14 :         std::string(), "dim0", std::string(), std::string(),
   14490           7 :         m_anValuesUInt32.size()));
   14491           7 : }
   14492             : 
   14493             : const std::vector<std::shared_ptr<GDALDimension>> &
   14494          14 : GDALAttributeNumeric::GetDimensions() const
   14495             : {
   14496          14 :     return m_dims;
   14497             : }
   14498             : 
   14499           8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
   14500             : {
   14501           8 :     return m_dt;
   14502             : }
   14503             : 
   14504           4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
   14505             :                                  const size_t *count, const GInt64 *arrayStep,
   14506             :                                  const GPtrDiff_t *bufferStride,
   14507             :                                  const GDALExtendedDataType &bufferDataType,
   14508             :                                  void *pDstBuffer) const
   14509             : {
   14510           4 :     if (m_dims.empty())
   14511             :     {
   14512           3 :         if (m_dt.GetNumericDataType() == GDT_Float64)
   14513           0 :             GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
   14514             :                                             bufferDataType);
   14515             :         else
   14516             :         {
   14517           3 :             CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
   14518           3 :             GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
   14519             :                                             bufferDataType);
   14520             :         }
   14521             :     }
   14522             :     else
   14523             :     {
   14524           1 :         CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
   14525           1 :         GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
   14526          30 :         for (size_t i = 0; i < count[0]; ++i)
   14527             :         {
   14528          29 :             GDALExtendedDataType::CopyValue(
   14529          29 :                 &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
   14530          29 :                                                       i * arrayStep[0])],
   14531          29 :                 m_dt, pabyDstBuffer, bufferDataType);
   14532          29 :             pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
   14533             :         }
   14534             :     }
   14535           4 :     return true;
   14536             : }
   14537             : 
   14538         224 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
   14539             :     const std::string &osParentName, const std::string &osName,
   14540             :     const std::shared_ptr<GDALDimension> &poDim, double dfStart,
   14541         224 :     double dfIncrement, double dfOffsetInIncrement)
   14542             :     : GDALAbstractMDArray(osParentName, osName),
   14543             :       GDALMDArray(osParentName, osName), m_dfStart(dfStart),
   14544             :       m_dfIncrement(dfIncrement),
   14545         448 :       m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
   14546             : {
   14547         224 : }
   14548             : 
   14549         224 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
   14550             :     const std::string &osParentName, const std::string &osName,
   14551             :     const std::shared_ptr<GDALDimension> &poDim, double dfStart,
   14552             :     double dfIncrement, double dfOffsetInIncrement)
   14553             : {
   14554             :     auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
   14555         224 :         osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
   14556         224 :     poArray->SetSelf(poArray);
   14557         224 :     return poArray;
   14558             : }
   14559             : 
   14560             : const std::vector<std::shared_ptr<GDALDimension>> &
   14561        1351 : GDALMDArrayRegularlySpaced::GetDimensions() const
   14562             : {
   14563        1351 :     return m_dims;
   14564             : }
   14565             : 
   14566         570 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
   14567             : {
   14568         570 :     return m_dt;
   14569             : }
   14570             : 
   14571             : std::vector<std::shared_ptr<GDALAttribute>>
   14572           4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
   14573             : {
   14574           4 :     return m_attributes;
   14575             : }
   14576             : 
   14577           0 : void GDALMDArrayRegularlySpaced::AddAttribute(
   14578             :     const std::shared_ptr<GDALAttribute> &poAttr)
   14579             : {
   14580           0 :     m_attributes.emplace_back(poAttr);
   14581           0 : }
   14582             : 
   14583         293 : bool GDALMDArrayRegularlySpaced::IRead(
   14584             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
   14585             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
   14586             :     void *pDstBuffer) const
   14587             : {
   14588         293 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
   14589       16237 :     for (size_t i = 0; i < count[0]; i++)
   14590             :     {
   14591       15944 :         const double dfVal =
   14592       15944 :             m_dfStart +
   14593       15944 :             (arrayStartIdx[0] + i * static_cast<double>(arrayStep[0]) +
   14594       15944 :              m_dfOffsetInIncrement) *
   14595       15944 :                 m_dfIncrement;
   14596       15944 :         GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
   14597             :                                         bufferDataType);
   14598       15944 :         pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
   14599             :     }
   14600         293 :     return true;
   14601             : }
   14602             : 
   14603        3999 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
   14604             :     const std::string &osParentName, const std::string &osName,
   14605        3999 :     const std::string &osType, const std::string &osDirection, GUInt64 nSize)
   14606        3999 :     : GDALDimension(osParentName, osName, osType, osDirection, nSize)
   14607             : {
   14608        3999 : }
   14609             : 
   14610             : std::shared_ptr<GDALMDArray>
   14611        1459 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
   14612             : {
   14613        1459 :     return m_poIndexingVariable.lock();
   14614             : }
   14615             : 
   14616             : // cppcheck-suppress passedByValue
   14617         644 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
   14618             :     std::shared_ptr<GDALMDArray> poIndexingVariable)
   14619             : {
   14620         644 :     m_poIndexingVariable = poIndexingVariable;
   14621         644 :     return true;
   14622             : }
   14623             : 
   14624          33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
   14625             : {
   14626          33 :     m_nSize = nNewSize;
   14627          33 : }
   14628             : 
   14629             : /************************************************************************/
   14630             : /*                       GDALPamMultiDim::Private                       */
   14631             : /************************************************************************/
   14632             : 
   14633             : struct GDALPamMultiDim::Private
   14634             : {
   14635             :     std::string m_osFilename{};
   14636             :     std::string m_osPamFilename{};
   14637             : 
   14638             :     struct Statistics
   14639             :     {
   14640             :         bool bHasStats = false;
   14641             :         bool bApproxStats = false;
   14642             :         double dfMin = 0;
   14643             :         double dfMax = 0;
   14644             :         double dfMean = 0;
   14645             :         double dfStdDev = 0;
   14646             :         GUInt64 nValidCount = 0;
   14647             :     };
   14648             : 
   14649             :     struct ArrayInfo
   14650             :     {
   14651             :         std::shared_ptr<OGRSpatialReference> poSRS{};
   14652             :         // cppcheck-suppress unusedStructMember
   14653             :         Statistics stats{};
   14654             :     };
   14655             : 
   14656             :     typedef std::pair<std::string, std::string> NameContext;
   14657             :     std::map<NameContext, ArrayInfo> m_oMapArray{};
   14658             :     std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
   14659             :     bool m_bDirty = false;
   14660             :     bool m_bLoaded = false;
   14661             : };
   14662             : 
   14663             : /************************************************************************/
   14664             : /*                          GDALPamMultiDim                             */
   14665             : /************************************************************************/
   14666             : 
   14667        1636 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
   14668        1636 :     : d(new Private())
   14669             : {
   14670        1636 :     d->m_osFilename = osFilename;
   14671        1636 : }
   14672             : 
   14673             : /************************************************************************/
   14674             : /*                   GDALPamMultiDim::~GDALPamMultiDim()                */
   14675             : /************************************************************************/
   14676             : 
   14677        1636 : GDALPamMultiDim::~GDALPamMultiDim()
   14678             : {
   14679        1636 :     if (d->m_bDirty)
   14680          30 :         Save();
   14681        1636 : }
   14682             : 
   14683             : /************************************************************************/
   14684             : /*                          GDALPamMultiDim::Load()                     */
   14685             : /************************************************************************/
   14686             : 
   14687         107 : void GDALPamMultiDim::Load()
   14688             : {
   14689         107 :     if (d->m_bLoaded)
   14690          96 :         return;
   14691          45 :     d->m_bLoaded = true;
   14692             : 
   14693          45 :     const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
   14694          45 :     d->m_osPamFilename =
   14695          90 :         pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
   14696          45 :     CPLXMLTreeCloser oTree(nullptr);
   14697             :     {
   14698          90 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
   14699          45 :         oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
   14700             :     }
   14701          45 :     if (!oTree)
   14702             :     {
   14703          34 :         return;
   14704             :     }
   14705          11 :     const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
   14706          11 :     if (!poPAMMultiDim)
   14707           0 :         return;
   14708          35 :     for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
   14709          24 :          psIter = psIter->psNext)
   14710             :     {
   14711          24 :         if (psIter->eType == CXT_Element &&
   14712          24 :             strcmp(psIter->pszValue, "Array") == 0)
   14713             :         {
   14714          13 :             const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
   14715          13 :             if (!pszName)
   14716           0 :                 continue;
   14717          13 :             const char *pszContext = CPLGetXMLValue(psIter, "context", "");
   14718             :             const auto oKey =
   14719          26 :                 std::pair<std::string, std::string>(pszName, pszContext);
   14720             : 
   14721             :             /* --------------------------------------------------------------------
   14722             :              */
   14723             :             /*      Check for an SRS node. */
   14724             :             /* --------------------------------------------------------------------
   14725             :              */
   14726          13 :             const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
   14727          13 :             if (psSRSNode)
   14728             :             {
   14729             :                 std::shared_ptr<OGRSpatialReference> poSRS =
   14730           6 :                     std::make_shared<OGRSpatialReference>();
   14731           3 :                 poSRS->SetFromUserInput(
   14732             :                     CPLGetXMLValue(psSRSNode, nullptr, ""),
   14733             :                     OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
   14734           3 :                 const char *pszMapping = CPLGetXMLValue(
   14735             :                     psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
   14736           3 :                 if (pszMapping)
   14737             :                 {
   14738             :                     char **papszTokens =
   14739           3 :                         CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
   14740           6 :                     std::vector<int> anMapping;
   14741           9 :                     for (int i = 0; papszTokens && papszTokens[i]; i++)
   14742             :                     {
   14743           6 :                         anMapping.push_back(atoi(papszTokens[i]));
   14744             :                     }
   14745           3 :                     CSLDestroy(papszTokens);
   14746           3 :                     poSRS->SetDataAxisToSRSAxisMapping(anMapping);
   14747             :                 }
   14748             :                 else
   14749             :                 {
   14750           0 :                     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
   14751             :                 }
   14752             : 
   14753             :                 const char *pszCoordinateEpoch =
   14754           3 :                     CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
   14755           3 :                 if (pszCoordinateEpoch)
   14756           3 :                     poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
   14757             : 
   14758           3 :                 d->m_oMapArray[oKey].poSRS = std::move(poSRS);
   14759             :             }
   14760             : 
   14761             :             const CPLXMLNode *psStatistics =
   14762          13 :                 CPLGetXMLNode(psIter, "Statistics");
   14763          13 :             if (psStatistics)
   14764             :             {
   14765           7 :                 Private::Statistics sStats;
   14766           7 :                 sStats.bHasStats = true;
   14767           7 :                 sStats.bApproxStats = CPLTestBool(
   14768             :                     CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
   14769           7 :                 sStats.dfMin =
   14770           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
   14771           7 :                 sStats.dfMax =
   14772           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
   14773           7 :                 sStats.dfMean =
   14774           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
   14775           7 :                 sStats.dfStdDev =
   14776           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
   14777           7 :                 sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
   14778             :                     CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
   14779           7 :                 d->m_oMapArray[oKey].stats = sStats;
   14780          13 :             }
   14781             :         }
   14782             :         else
   14783             :         {
   14784          11 :             CPLXMLNode *psNextBackup = psIter->psNext;
   14785          11 :             psIter->psNext = nullptr;
   14786          11 :             d->m_apoOtherNodes.emplace_back(
   14787          11 :                 CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
   14788          11 :             psIter->psNext = psNextBackup;
   14789             :         }
   14790             :     }
   14791             : }
   14792             : 
   14793             : /************************************************************************/
   14794             : /*                          GDALPamMultiDim::Save()                     */
   14795             : /************************************************************************/
   14796             : 
   14797          30 : void GDALPamMultiDim::Save()
   14798             : {
   14799             :     CPLXMLTreeCloser oTree(
   14800          60 :         CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
   14801          34 :     for (const auto &poOtherNode : d->m_apoOtherNodes)
   14802             :     {
   14803           4 :         CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
   14804             :     }
   14805         112 :     for (const auto &kv : d->m_oMapArray)
   14806             :     {
   14807             :         CPLXMLNode *psArrayNode =
   14808          82 :             CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
   14809          82 :         CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
   14810          82 :         if (!kv.first.second.empty())
   14811             :         {
   14812           1 :             CPLAddXMLAttributeAndValue(psArrayNode, "context",
   14813             :                                        kv.first.second.c_str());
   14814             :         }
   14815          82 :         if (kv.second.poSRS)
   14816             :         {
   14817          71 :             char *pszWKT = nullptr;
   14818             :             {
   14819         142 :                 CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
   14820          71 :                 const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
   14821          71 :                 kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
   14822             :             }
   14823             :             CPLXMLNode *psSRSNode =
   14824          71 :                 CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
   14825          71 :             CPLFree(pszWKT);
   14826             :             const auto &mapping =
   14827          71 :                 kv.second.poSRS->GetDataAxisToSRSAxisMapping();
   14828         142 :             CPLString osMapping;
   14829         213 :             for (size_t i = 0; i < mapping.size(); ++i)
   14830             :             {
   14831         142 :                 if (!osMapping.empty())
   14832          71 :                     osMapping += ",";
   14833         142 :                 osMapping += CPLSPrintf("%d", mapping[i]);
   14834             :             }
   14835          71 :             CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
   14836             :                                        osMapping.c_str());
   14837             : 
   14838             :             const double dfCoordinateEpoch =
   14839          71 :                 kv.second.poSRS->GetCoordinateEpoch();
   14840          71 :             if (dfCoordinateEpoch > 0)
   14841             :             {
   14842             :                 std::string osCoordinateEpoch =
   14843           2 :                     CPLSPrintf("%f", dfCoordinateEpoch);
   14844           1 :                 if (osCoordinateEpoch.find('.') != std::string::npos)
   14845             :                 {
   14846           6 :                     while (osCoordinateEpoch.back() == '0')
   14847           5 :                         osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
   14848             :                 }
   14849           1 :                 CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
   14850             :                                            osCoordinateEpoch.c_str());
   14851             :             }
   14852             :         }
   14853             : 
   14854          82 :         if (kv.second.stats.bHasStats)
   14855             :         {
   14856             :             CPLXMLNode *psMDArray =
   14857           8 :                 CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
   14858           8 :             CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
   14859           8 :                                         kv.second.stats.bApproxStats ? "1"
   14860             :                                                                      : "0");
   14861           8 :             CPLCreateXMLElementAndValue(
   14862             :                 psMDArray, "Minimum",
   14863           8 :                 CPLSPrintf("%.17g", kv.second.stats.dfMin));
   14864           8 :             CPLCreateXMLElementAndValue(
   14865             :                 psMDArray, "Maximum",
   14866           8 :                 CPLSPrintf("%.17g", kv.second.stats.dfMax));
   14867           8 :             CPLCreateXMLElementAndValue(
   14868           8 :                 psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
   14869           8 :             CPLCreateXMLElementAndValue(
   14870             :                 psMDArray, "StdDev",
   14871           8 :                 CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
   14872           8 :             CPLCreateXMLElementAndValue(
   14873             :                 psMDArray, "ValidSampleCount",
   14874           8 :                 CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
   14875             :         }
   14876             :     }
   14877             : 
   14878             :     int bSaved;
   14879          60 :     CPLErrorAccumulator oErrorAccumulator;
   14880             :     {
   14881          30 :         auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
   14882          30 :         CPL_IGNORE_RET_VAL(oAccumulator);
   14883             :         bSaved =
   14884          30 :             CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
   14885             :     }
   14886             : 
   14887          30 :     const char *pszNewPam = nullptr;
   14888          30 :     if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
   14889           0 :         ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
   14890             :     {
   14891           0 :         CPLErrorReset();
   14892           0 :         CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
   14893             :     }
   14894             :     else
   14895             :     {
   14896          30 :         oErrorAccumulator.ReplayErrors();
   14897             :     }
   14898          30 : }
   14899             : 
   14900             : /************************************************************************/
   14901             : /*                    GDALPamMultiDim::GetSpatialRef()                  */
   14902             : /************************************************************************/
   14903             : 
   14904             : std::shared_ptr<OGRSpatialReference>
   14905          10 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
   14906             :                                const std::string &osContext)
   14907             : {
   14908          10 :     Load();
   14909             :     auto oIter =
   14910          10 :         d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
   14911          10 :     if (oIter != d->m_oMapArray.end())
   14912           2 :         return oIter->second.poSRS;
   14913           8 :     return nullptr;
   14914             : }
   14915             : 
   14916             : /************************************************************************/
   14917             : /*                    GDALPamMultiDim::SetSpatialRef()                  */
   14918             : /************************************************************************/
   14919             : 
   14920          72 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
   14921             :                                     const std::string &osContext,
   14922             :                                     const OGRSpatialReference *poSRS)
   14923             : {
   14924          72 :     Load();
   14925          72 :     d->m_bDirty = true;
   14926          72 :     if (poSRS && !poSRS->IsEmpty())
   14927          71 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
   14928             :             poSRS->Clone());
   14929             :     else
   14930           2 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
   14931           1 :             .poSRS.reset();
   14932          72 : }
   14933             : 
   14934             : /************************************************************************/
   14935             : /*                           GetStatistics()                            */
   14936             : /************************************************************************/
   14937             : 
   14938          16 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
   14939             :                                       const std::string &osContext,
   14940             :                                       bool bApproxOK, double *pdfMin,
   14941             :                                       double *pdfMax, double *pdfMean,
   14942             :                                       double *pdfStdDev, GUInt64 *pnValidCount)
   14943             : {
   14944          16 :     Load();
   14945             :     auto oIter =
   14946          16 :         d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
   14947          16 :     if (oIter == d->m_oMapArray.end())
   14948           9 :         return CE_Failure;
   14949           7 :     const auto &stats = oIter->second.stats;
   14950           7 :     if (!stats.bHasStats)
   14951           1 :         return CE_Failure;
   14952           6 :     if (!bApproxOK && stats.bApproxStats)
   14953           0 :         return CE_Failure;
   14954           6 :     if (pdfMin)
   14955           6 :         *pdfMin = stats.dfMin;
   14956           6 :     if (pdfMax)
   14957           6 :         *pdfMax = stats.dfMax;
   14958           6 :     if (pdfMean)
   14959           6 :         *pdfMean = stats.dfMean;
   14960           6 :     if (pdfStdDev)
   14961           6 :         *pdfStdDev = stats.dfStdDev;
   14962           6 :     if (pnValidCount)
   14963           6 :         *pnValidCount = stats.nValidCount;
   14964           6 :     return CE_None;
   14965             : }
   14966             : 
   14967             : /************************************************************************/
   14968             : /*                           SetStatistics()                            */
   14969             : /************************************************************************/
   14970             : 
   14971           8 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
   14972             :                                     const std::string &osContext,
   14973             :                                     bool bApproxStats, double dfMin,
   14974             :                                     double dfMax, double dfMean,
   14975             :                                     double dfStdDev, GUInt64 nValidCount)
   14976             : {
   14977           8 :     Load();
   14978           8 :     d->m_bDirty = true;
   14979             :     auto &stats =
   14980           8 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
   14981           8 :     stats.bHasStats = true;
   14982           8 :     stats.bApproxStats = bApproxStats;
   14983           8 :     stats.dfMin = dfMin;
   14984           8 :     stats.dfMax = dfMax;
   14985           8 :     stats.dfMean = dfMean;
   14986           8 :     stats.dfStdDev = dfStdDev;
   14987           8 :     stats.nValidCount = nValidCount;
   14988           8 : }
   14989             : 
   14990             : /************************************************************************/
   14991             : /*                           ClearStatistics()                          */
   14992             : /************************************************************************/
   14993             : 
   14994           0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
   14995             :                                       const std::string &osContext)
   14996             : {
   14997           0 :     Load();
   14998           0 :     d->m_bDirty = true;
   14999           0 :     d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
   15000             :         false;
   15001           0 : }
   15002             : 
   15003             : /************************************************************************/
   15004             : /*                           ClearStatistics()                          */
   15005             : /************************************************************************/
   15006             : 
   15007           1 : void GDALPamMultiDim::ClearStatistics()
   15008             : {
   15009           1 :     Load();
   15010           1 :     d->m_bDirty = true;
   15011           3 :     for (auto &kv : d->m_oMapArray)
   15012           2 :         kv.second.stats.bHasStats = false;
   15013           1 : }
   15014             : 
   15015             : /************************************************************************/
   15016             : /*                             GetPAM()                                 */
   15017             : /************************************************************************/
   15018             : 
   15019             : /*static*/ std::shared_ptr<GDALPamMultiDim>
   15020         939 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
   15021             : {
   15022         939 :     auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
   15023         939 :     if (poPamArray)
   15024         581 :         return poPamArray->GetPAM();
   15025         358 :     return nullptr;
   15026             : }
   15027             : 
   15028             : /************************************************************************/
   15029             : /*                           GDALPamMDArray                             */
   15030             : /************************************************************************/
   15031             : 
   15032        4498 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
   15033             :                                const std::string &osName,
   15034             :                                const std::shared_ptr<GDALPamMultiDim> &poPam,
   15035           0 :                                const std::string &osContext)
   15036             :     :
   15037             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
   15038             :       GDALAbstractMDArray(osParentName, osName),
   15039             : #endif
   15040        4498 :       GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
   15041             : {
   15042        4498 : }
   15043             : 
   15044             : /************************************************************************/
   15045             : /*                    GDALPamMDArray::SetSpatialRef()                   */
   15046             : /************************************************************************/
   15047             : 
   15048          72 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
   15049             : {
   15050          72 :     if (!m_poPam)
   15051           0 :         return false;
   15052          72 :     m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
   15053          72 :     return true;
   15054             : }
   15055             : 
   15056             : /************************************************************************/
   15057             : /*                    GDALPamMDArray::GetSpatialRef()                   */
   15058             : /************************************************************************/
   15059             : 
   15060          10 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
   15061             : {
   15062          10 :     if (!m_poPam)
   15063           0 :         return nullptr;
   15064          10 :     return m_poPam->GetSpatialRef(GetFullName(), GetContext());
   15065             : }
   15066             : 
   15067             : /************************************************************************/
   15068             : /*                           GetStatistics()                            */
   15069             : /************************************************************************/
   15070             : 
   15071          16 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
   15072             :                                      double *pdfMin, double *pdfMax,
   15073             :                                      double *pdfMean, double *pdfStdDev,
   15074             :                                      GUInt64 *pnValidCount,
   15075             :                                      GDALProgressFunc pfnProgress,
   15076             :                                      void *pProgressData)
   15077             : {
   15078          16 :     if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
   15079             :                                           bApproxOK, pdfMin, pdfMax, pdfMean,
   15080          16 :                                           pdfStdDev, pnValidCount) == CE_None)
   15081             :     {
   15082           6 :         return CE_None;
   15083             :     }
   15084          10 :     if (!bForce)
   15085           4 :         return CE_Warning;
   15086             : 
   15087           6 :     return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
   15088             :                                       pdfMean, pdfStdDev, pnValidCount,
   15089           6 :                                       pfnProgress, pProgressData);
   15090             : }
   15091             : 
   15092             : /************************************************************************/
   15093             : /*                           SetStatistics()                            */
   15094             : /************************************************************************/
   15095             : 
   15096           8 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
   15097             :                                    double dfMax, double dfMean, double dfStdDev,
   15098             :                                    GUInt64 nValidCount,
   15099             :                                    CSLConstList /* papszOptions */)
   15100             : {
   15101           8 :     if (!m_poPam)
   15102           0 :         return false;
   15103           8 :     m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
   15104             :                            dfMax, dfMean, dfStdDev, nValidCount);
   15105           8 :     return true;
   15106             : }
   15107             : 
   15108             : /************************************************************************/
   15109             : /*                           ClearStatistics()                          */
   15110             : /************************************************************************/
   15111             : 
   15112           0 : void GDALPamMDArray::ClearStatistics()
   15113             : {
   15114           0 :     if (!m_poPam)
   15115           0 :         return;
   15116           0 :     m_poPam->ClearStatistics(GetFullName(), GetContext());
   15117             : }
   15118             : 
   15119             : /************************************************************************/
   15120             : /*                       GDALMDIAsAttribute::GetDimensions()            */
   15121             : /************************************************************************/
   15122             : 
   15123             : const std::vector<std::shared_ptr<GDALDimension>> &
   15124          29 : GDALMDIAsAttribute::GetDimensions() const
   15125             : {
   15126          29 :     return m_dims;
   15127             : }
   15128             : 
   15129             : /************************************************************************/
   15130             : /*           GDALMDArrayRawBlockInfo::~GDALMDArrayRawBlockInfo()        */
   15131             : /************************************************************************/
   15132             : 
   15133          58 : GDALMDArrayRawBlockInfo::~GDALMDArrayRawBlockInfo()
   15134             : {
   15135          29 :     clear();
   15136          29 : }
   15137             : 
   15138             : /************************************************************************/
   15139             : /*                     GDALMDArrayRawBlockInfo::clear()                 */
   15140             : /************************************************************************/
   15141             : 
   15142          47 : void GDALMDArrayRawBlockInfo::clear()
   15143             : {
   15144          47 :     CPLFree(pszFilename);
   15145          47 :     pszFilename = nullptr;
   15146          47 :     CSLDestroy(papszInfo);
   15147          47 :     papszInfo = nullptr;
   15148          47 :     nOffset = 0;
   15149          47 :     nSize = 0;
   15150          47 :     CPLFree(pabyInlineData);
   15151          47 :     pabyInlineData = nullptr;
   15152          47 : }
   15153             : 
   15154             : /************************************************************************/
   15155             : /*            GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo()        */
   15156             : /************************************************************************/
   15157             : 
   15158           4 : GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo(
   15159           4 :     const GDALMDArrayRawBlockInfo &other)
   15160           4 :     : pszFilename(other.pszFilename ? CPLStrdup(other.pszFilename) : nullptr),
   15161           4 :       nOffset(other.nOffset), nSize(other.nSize),
   15162           4 :       papszInfo(CSLDuplicate(other.papszInfo)), pabyInlineData(nullptr)
   15163             : {
   15164           4 :     if (other.pabyInlineData)
   15165             :     {
   15166           3 :         pabyInlineData = static_cast<GByte *>(
   15167           3 :             VSI_MALLOC_VERBOSE(static_cast<size_t>(other.nSize)));
   15168           3 :         if (pabyInlineData)
   15169           3 :             memcpy(pabyInlineData, other.pabyInlineData,
   15170           3 :                    static_cast<size_t>(other.nSize));
   15171             :     }
   15172           4 : }
   15173             : 
   15174             : /************************************************************************/
   15175             : /*                GDALMDArrayRawBlockInfo::operator=()                  */
   15176             : /************************************************************************/
   15177             : 
   15178             : GDALMDArrayRawBlockInfo &
   15179           7 : GDALMDArrayRawBlockInfo::operator=(const GDALMDArrayRawBlockInfo &other)
   15180             : {
   15181           7 :     if (this != &other)
   15182             :     {
   15183           5 :         CPLFree(pszFilename);
   15184           5 :         pszFilename =
   15185           5 :             other.pszFilename ? CPLStrdup(other.pszFilename) : nullptr;
   15186           5 :         nOffset = other.nOffset;
   15187           5 :         nSize = other.nSize;
   15188           5 :         CSLDestroy(papszInfo);
   15189           5 :         papszInfo = CSLDuplicate(other.papszInfo);
   15190           5 :         CPLFree(pabyInlineData);
   15191           5 :         pabyInlineData = nullptr;
   15192           5 :         if (other.pabyInlineData)
   15193             :         {
   15194           4 :             pabyInlineData = static_cast<GByte *>(
   15195           4 :                 VSI_MALLOC_VERBOSE(static_cast<size_t>(other.nSize)));
   15196           4 :             if (pabyInlineData)
   15197           4 :                 memcpy(pabyInlineData, other.pabyInlineData,
   15198           4 :                        static_cast<size_t>(other.nSize));
   15199             :         }
   15200             :     }
   15201           7 :     return *this;
   15202             : }
   15203             : 
   15204             : /************************************************************************/
   15205             : /*            GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo()        */
   15206             : /************************************************************************/
   15207             : 
   15208           2 : GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo(
   15209           2 :     GDALMDArrayRawBlockInfo &&other)
   15210           2 :     : pszFilename(other.pszFilename), nOffset(other.nOffset),
   15211           2 :       nSize(other.nSize), papszInfo(other.papszInfo),
   15212           2 :       pabyInlineData(other.pabyInlineData)
   15213             : {
   15214           2 :     other.pszFilename = nullptr;
   15215           2 :     other.papszInfo = nullptr;
   15216           2 :     other.pabyInlineData = nullptr;
   15217           2 : }
   15218             : 
   15219             : /************************************************************************/
   15220             : /*                GDALMDArrayRawBlockInfo::operator=()                  */
   15221             : /************************************************************************/
   15222             : 
   15223             : GDALMDArrayRawBlockInfo &
   15224           2 : GDALMDArrayRawBlockInfo::operator=(GDALMDArrayRawBlockInfo &&other)
   15225             : {
   15226           2 :     if (this != &other)
   15227             :     {
   15228           2 :         std::swap(pszFilename, other.pszFilename);
   15229           2 :         nOffset = other.nOffset;
   15230           2 :         nSize = other.nSize;
   15231           2 :         std::swap(papszInfo, other.papszInfo);
   15232           2 :         std::swap(pabyInlineData, other.pabyInlineData);
   15233             :     }
   15234           2 :     return *this;
   15235             : }
   15236             : 
   15237             : //! @endcond
   15238             : 
   15239             : /************************************************************************/
   15240             : /*                       GDALMDArray::GetRawBlockInfo()                 */
   15241             : /************************************************************************/
   15242             : 
   15243             : /** Return information on a raw block.
   15244             :  *
   15245             :  * The block coordinates must be between 0 and
   15246             :  * (GetDimensions()[i]->GetSize() / GetBlockSize()[i]) - 1, for all i between
   15247             :  * 0 and GetDimensionCount()-1.
   15248             :  *
   15249             :  * If the queried block has valid coordinates but is missing in the dataset,
   15250             :  * all fields of info will be set to 0/nullptr, but the function will return
   15251             :  * true.
   15252             :  *
   15253             :  * This method is only implemented by a subset of drivers. The base
   15254             :  * implementation just returns false and empty info.
   15255             :  *
   15256             :  * The values returned in psBlockInfo->papszInfo are driver dependent.
   15257             :  *
   15258             :  * For multi-byte data types, drivers should return a "ENDIANNESS" key whose
   15259             :  * value is "LITTLE" or "BIG".
   15260             :  *
   15261             :  * For HDF5 and netCDF 4, the potential keys are "COMPRESSION" (possible values
   15262             :  * "DEFLATE" or "SZIP") and "FILTER" (if several filters, names are
   15263             :  * comma-separated)
   15264             :  *
   15265             :  * For ZARR, the potential keys are "COMPRESSOR" (value is the JSON encoded
   15266             :  * content from the array definition), "FILTERS" (for Zarr V2, value is JSON
   15267             :  * encoded content) and "TRANSPOSE_ORDER" (value is a string like
   15268             :  * "[idx0,...,idxN]" with the permutation).
   15269             :  *
   15270             :  * For VRT, the potential keys are the ones of the underlying source(s). Note
   15271             :  * that GetRawBlockInfo() on VRT only works when the VRT declares a block size,
   15272             :  * that for each queried VRT block, there is one and only one source that
   15273             :  * is used to fill the VRT block and that the block size of this source is
   15274             :  * exactly the one of the VRT block.
   15275             :  *
   15276             :  * This is the same as C function GDALMDArrayGetRawBlockInfo().
   15277             :  *
   15278             :  * @param panBlockCoordinates array of GetDimensionCount() values with the block
   15279             :  *                            coordinates.
   15280             :  * @param[out] info structure to fill with block information.
   15281             :  * @return true in case of success, or false if an error occurs.
   15282             :  * @since 3.12
   15283             :  */
   15284           0 : bool GDALMDArray::GetRawBlockInfo(const uint64_t *panBlockCoordinates,
   15285             :                                   GDALMDArrayRawBlockInfo &info) const
   15286             : {
   15287             :     (void)panBlockCoordinates;
   15288           0 :     info.clear();
   15289           0 :     return false;
   15290             : }
   15291             : 
   15292             : /************************************************************************/
   15293             : /*                      GDALMDArrayGetRawBlockInfo()                    */
   15294             : /************************************************************************/
   15295             : 
   15296             : /** Return information on a raw block.
   15297             :  *
   15298             :  * The block coordinates must be between 0 and
   15299             :  * (GetDimensions()[i]->GetSize() / GetBlockSize()[i]) - 1, for all i between
   15300             :  * 0 and GetDimensionCount()-1.
   15301             :  *
   15302             :  * If the queried block has valid coordinates but is missing in the dataset,
   15303             :  * all fields of info will be set to 0/nullptr, but the function will return
   15304             :  * true.
   15305             :  *
   15306             :  * This method is only implemented by a subset of drivers. The base
   15307             :  * implementation just returns false and empty info.
   15308             :  *
   15309             :  * The values returned in psBlockInfo->papszInfo are driver dependent.
   15310             :  *
   15311             :  * For multi-byte data types, drivers should return a "ENDIANNESS" key whose
   15312             :  * value is "LITTLE" or "BIG".
   15313             :  *
   15314             :  * For HDF5 and netCDF 4, the potential keys are "COMPRESSION" (possible values
   15315             :  * "DEFLATE" or "SZIP") and "FILTER" (if several filters, names are
   15316             :  * comma-separated)
   15317             :  *
   15318             :  * For ZARR, the potential keys are "COMPRESSOR" (value is the JSON encoded
   15319             :  * content from the array definition), "FILTERS" (for Zarr V2, value is JSON
   15320             :  * encoded content) and "TRANSPOSE_ORDER" (value is a string like
   15321             :  * "[idx0,...,idxN]" with the permutation).
   15322             :  *
   15323             :  * For VRT, the potential keys are the ones of the underlying source(s). Note
   15324             :  * that GetRawBlockInfo() on VRT only works when the VRT declares a block size,
   15325             :  * that for each queried VRT block, there is one and only one source that
   15326             :  * is used to fill the VRT block and that the block size of this source is
   15327             :  * exactly the one of the VRT block.
   15328             :  *
   15329             :  * This is the same as C++ method GDALMDArray::GetRawBlockInfo().
   15330             :  *
   15331             :  * @param hArray handle to array.
   15332             :  * @param panBlockCoordinates array of GetDimensionCount() values with the block
   15333             :  *                            coordinates.
   15334             :  * @param[out] psBlockInfo structure to fill with block information.
   15335             :  *                         Must be allocated with GDALMDArrayRawBlockInfoCreate(),
   15336             :  *                         and freed with GDALMDArrayRawBlockInfoRelease().
   15337             :  * @return true in case of success, or false if an error occurs.
   15338             :  * @since 3.12
   15339             :  */
   15340          18 : bool GDALMDArrayGetRawBlockInfo(GDALMDArrayH hArray,
   15341             :                                 const uint64_t *panBlockCoordinates,
   15342             :                                 GDALMDArrayRawBlockInfo *psBlockInfo)
   15343             : {
   15344          18 :     VALIDATE_POINTER1(hArray, __func__, false);
   15345          18 :     VALIDATE_POINTER1(panBlockCoordinates, __func__, false);
   15346          18 :     VALIDATE_POINTER1(psBlockInfo, __func__, false);
   15347          18 :     return hArray->m_poImpl->GetRawBlockInfo(panBlockCoordinates, *psBlockInfo);
   15348             : }
   15349             : 
   15350             : /************************************************************************/
   15351             : /*                    GDALMDArrayRawBlockInfoCreate()                   */
   15352             : /************************************************************************/
   15353             : 
   15354             : /** Allocate a new instance of GDALMDArrayRawBlockInfo.
   15355             :  *
   15356             :  * Returned pointer must be freed with GDALMDArrayRawBlockInfoRelease().
   15357             :  *
   15358             :  * @since 3.12
   15359             :  */
   15360          18 : GDALMDArrayRawBlockInfo *GDALMDArrayRawBlockInfoCreate(void)
   15361             : {
   15362          18 :     return new GDALMDArrayRawBlockInfo();
   15363             : }
   15364             : 
   15365             : /************************************************************************/
   15366             : /*                    GDALMDArrayRawBlockInfoRelease()                  */
   15367             : /************************************************************************/
   15368             : 
   15369             : /** Free an instance of GDALMDArrayRawBlockInfo.
   15370             :  *
   15371             :  * @since 3.12
   15372             :  */
   15373          18 : void GDALMDArrayRawBlockInfoRelease(GDALMDArrayRawBlockInfo *psBlockInfo)
   15374             : {
   15375          18 :     delete psBlockInfo;
   15376          18 : }

Generated by: LCOV version 1.14