LCOV - code coverage report
Current view: top level - gcore - gdalmultidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 4755 5255 90.5 %
Date: 2025-05-31 00:00:17 Functions: 470 544 86.4 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Name:     gdalmultidim.cpp
       4             :  * Project:  GDAL Core
       5             :  * Purpose:  GDAL Core C++/Private implementation for multidimensional support
       6             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include <assert.h>
      15             : #include <algorithm>
      16             : #include <limits>
      17             : #include <list>
      18             : #include <queue>
      19             : #include <set>
      20             : #include <utility>
      21             : #include <time.h>
      22             : 
      23             : #include <cmath>
      24             : #include <ctype.h>  // isalnum
      25             : 
      26             : #include "cpl_error_internal.h"
      27             : #include "cpl_float.h"
      28             : #include "gdal_priv.h"
      29             : #include "gdal_pam.h"
      30             : #include "gdal_rat.h"
      31             : #include "gdal_utils.h"
      32             : #include "cpl_safemaths.hpp"
      33             : #include "memmultidim.h"
      34             : #include "ogrsf_frmts.h"
      35             : #include "gdalmultidim_priv.h"
      36             : 
      37             : #if defined(__clang__) || defined(_MSC_VER)
      38             : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
      39             : #endif
      40             : 
      41             : /************************************************************************/
      42             : /*                       GDALMDArrayUnscaled                            */
      43             : /************************************************************************/
      44             : 
      45             : class GDALMDArrayUnscaled final : public GDALPamMDArray
      46             : {
      47             :   private:
      48             :     std::shared_ptr<GDALMDArray> m_poParent{};
      49             :     const GDALExtendedDataType m_dt;
      50             :     bool m_bHasNoData;
      51             :     const double m_dfScale;
      52             :     const double m_dfOffset;
      53             :     std::vector<GByte> m_abyRawNoData{};
      54             : 
      55             :   protected:
      56          13 :     explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
      57             :                                  double dfScale, double dfOffset,
      58             :                                  double dfOverriddenDstNodata, GDALDataType eDT)
      59          26 :         : GDALAbstractMDArray(std::string(),
      60          26 :                               "Unscaled view of " + poParent->GetFullName()),
      61             :           GDALPamMDArray(
      62          26 :               std::string(), "Unscaled view of " + poParent->GetFullName(),
      63          26 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
      64          13 :           m_poParent(std::move(poParent)),
      65             :           m_dt(GDALExtendedDataType::Create(eDT)),
      66          13 :           m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
      67          78 :           m_dfScale(dfScale), m_dfOffset(dfOffset)
      68             :     {
      69          13 :         m_abyRawNoData.resize(m_dt.GetSize());
      70             :         const auto eNonComplexDT =
      71          13 :             GDALGetNonComplexDataType(m_dt.GetNumericDataType());
      72          26 :         GDALCopyWords64(
      73          13 :             &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
      74             :             eNonComplexDT, GDALGetDataTypeSizeBytes(eNonComplexDT),
      75          13 :             GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
      76          13 :     }
      77             : 
      78             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
      79             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      80             :                const GDALExtendedDataType &bufferDataType,
      81             :                void *pDstBuffer) const override;
      82             : 
      83             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
      84             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      85             :                 const GDALExtendedDataType &bufferDataType,
      86             :                 const void *pSrcBuffer) override;
      87             : 
      88           0 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
      89             :                      CSLConstList papszOptions) const override
      90             :     {
      91           0 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
      92             :     }
      93             : 
      94             :   public:
      95             :     static std::shared_ptr<GDALMDArrayUnscaled>
      96          13 :     Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
      97             :            double dfOffset, double dfDstNodata, GDALDataType eDT)
      98             :     {
      99             :         auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
     100          13 :             poParent, dfScale, dfOffset, dfDstNodata, eDT)));
     101          13 :         newAr->SetSelf(newAr);
     102          13 :         return newAr;
     103             :     }
     104             : 
     105           1 :     bool IsWritable() const override
     106             :     {
     107           1 :         return m_poParent->IsWritable();
     108             :     }
     109             : 
     110          15 :     const std::string &GetFilename() const override
     111             :     {
     112          15 :         return m_poParent->GetFilename();
     113             :     }
     114             : 
     115             :     const std::vector<std::shared_ptr<GDALDimension>> &
     116         220 :     GetDimensions() const override
     117             :     {
     118         220 :         return m_poParent->GetDimensions();
     119             :     }
     120             : 
     121         103 :     const GDALExtendedDataType &GetDataType() const override
     122             :     {
     123         103 :         return m_dt;
     124             :     }
     125             : 
     126           1 :     const std::string &GetUnit() const override
     127             :     {
     128           1 :         return m_poParent->GetUnit();
     129             :     }
     130             : 
     131           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
     132             :     {
     133           1 :         return m_poParent->GetSpatialRef();
     134             :     }
     135             : 
     136           6 :     const void *GetRawNoDataValue() const override
     137             :     {
     138           6 :         return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
     139             :     }
     140             : 
     141           1 :     bool SetRawNoDataValue(const void *pRawNoData) override
     142             :     {
     143           1 :         m_bHasNoData = true;
     144           1 :         memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
     145           1 :         return true;
     146             :     }
     147             : 
     148           4 :     std::vector<GUInt64> GetBlockSize() const override
     149             :     {
     150           4 :         return m_poParent->GetBlockSize();
     151             :     }
     152             : 
     153             :     std::shared_ptr<GDALAttribute>
     154           0 :     GetAttribute(const std::string &osName) const override
     155             :     {
     156           0 :         return m_poParent->GetAttribute(osName);
     157             :     }
     158             : 
     159             :     std::vector<std::shared_ptr<GDALAttribute>>
     160           1 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
     161             :     {
     162           1 :         return m_poParent->GetAttributes(papszOptions);
     163             :     }
     164             : 
     165           0 :     bool SetUnit(const std::string &osUnit) override
     166             :     {
     167           0 :         return m_poParent->SetUnit(osUnit);
     168             :     }
     169             : 
     170           0 :     bool SetSpatialRef(const OGRSpatialReference *poSRS) override
     171             :     {
     172           0 :         return m_poParent->SetSpatialRef(poSRS);
     173             :     }
     174             : 
     175             :     std::shared_ptr<GDALAttribute>
     176           1 :     CreateAttribute(const std::string &osName,
     177             :                     const std::vector<GUInt64> &anDimensions,
     178             :                     const GDALExtendedDataType &oDataType,
     179             :                     CSLConstList papszOptions = nullptr) override
     180             :     {
     181           1 :         return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
     182           1 :                                            papszOptions);
     183             :     }
     184             : };
     185             : 
     186             : /************************************************************************/
     187             : /*                         ~GDALIHasAttribute()                         */
     188             : /************************************************************************/
     189             : 
     190             : GDALIHasAttribute::~GDALIHasAttribute() = default;
     191             : 
     192             : /************************************************************************/
     193             : /*                            GetAttribute()                            */
     194             : /************************************************************************/
     195             : 
     196             : /** Return an attribute by its name.
     197             :  *
     198             :  * If the attribute does not exist, nullptr should be silently returned.
     199             :  *
     200             :  * @note Driver implementation: this method will fallback to
     201             :  * GetAttributeFromAttributes() is not explicitly implemented
     202             :  *
     203             :  * Drivers known to implement it for groups and arrays: MEM, netCDF.
     204             :  *
     205             :  * This is the same as the C function GDALGroupGetAttribute() or
     206             :  * GDALMDArrayGetAttribute().
     207             :  *
     208             :  * @param osName Attribute name
     209             :  * @return the attribute, or nullptr if it does not exist or an error occurred.
     210             :  */
     211             : std::shared_ptr<GDALAttribute>
     212        1055 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
     213             : {
     214        1055 :     return GetAttributeFromAttributes(osName);
     215             : }
     216             : 
     217             : /************************************************************************/
     218             : /*                       GetAttributeFromAttributes()                   */
     219             : /************************************************************************/
     220             : 
     221             : /** Possible fallback implementation for GetAttribute() using GetAttributes().
     222             :  */
     223             : std::shared_ptr<GDALAttribute>
     224        1055 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
     225             : {
     226        2110 :     auto attrs(GetAttributes());
     227        5491 :     for (const auto &attr : attrs)
     228             :     {
     229        5174 :         if (attr->GetName() == osName)
     230         738 :             return attr;
     231             :     }
     232         317 :     return nullptr;
     233             : }
     234             : 
     235             : /************************************************************************/
     236             : /*                           GetAttributes()                            */
     237             : /************************************************************************/
     238             : 
     239             : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
     240             :  *
     241             :  * If the attribute does not exist, nullptr should be silently returned.
     242             :  *
     243             :  * @note Driver implementation: optionally implemented. If implemented,
     244             :  * GetAttribute() should also be implemented.
     245             :  *
     246             :  * Drivers known to implement it for groups and arrays: MEM, netCDF.
     247             :  *
     248             :  * This is the same as the C function GDALGroupGetAttributes() or
     249             :  * GDALMDArrayGetAttributes().
     250             : 
     251             :  * @param papszOptions Driver specific options determining how attributes
     252             :  * should be retrieved. Pass nullptr for default behavior.
     253             :  *
     254             :  * @return the attributes.
     255             :  */
     256             : std::vector<std::shared_ptr<GDALAttribute>>
     257          42 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
     258             : {
     259          42 :     return {};
     260             : }
     261             : 
     262             : /************************************************************************/
     263             : /*                             CreateAttribute()                         */
     264             : /************************************************************************/
     265             : 
     266             : /** Create an attribute within a GDALMDArray or GDALGroup.
     267             :  *
     268             :  * The attribute might not be "physically" created until a value is written
     269             :  * into it.
     270             :  *
     271             :  * Optionally implemented.
     272             :  *
     273             :  * Drivers known to implement it: MEM, netCDF
     274             :  *
     275             :  * This is the same as the C function GDALGroupCreateAttribute() or
     276             :  * GDALMDArrayCreateAttribute()
     277             :  *
     278             :  * @param osName Attribute name.
     279             :  * @param anDimensions List of dimension sizes, ordered from the slowest varying
     280             :  *                     dimension first to the fastest varying dimension last.
     281             :  *                     Empty for a scalar attribute (common case)
     282             :  * @param oDataType  Attribute data type.
     283             :  * @param papszOptions Driver specific options determining how the attribute.
     284             :  * should be created.
     285             :  *
     286             :  * @return the new attribute, or nullptr if case of error
     287             :  */
     288           0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
     289             :     CPL_UNUSED const std::string &osName,
     290             :     CPL_UNUSED const std::vector<GUInt64> &anDimensions,
     291             :     CPL_UNUSED const GDALExtendedDataType &oDataType,
     292             :     CPL_UNUSED CSLConstList papszOptions)
     293             : {
     294           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     295             :              "CreateAttribute() not implemented");
     296           0 :     return nullptr;
     297             : }
     298             : 
     299             : /************************************************************************/
     300             : /*                          DeleteAttribute()                           */
     301             : /************************************************************************/
     302             : 
     303             : /** Delete an attribute from a GDALMDArray or GDALGroup.
     304             :  *
     305             :  * Optionally implemented.
     306             :  *
     307             :  * After this call, if a previously obtained instance of the deleted object
     308             :  * is still alive, no method other than for freeing it should be invoked.
     309             :  *
     310             :  * Drivers known to implement it: MEM, netCDF
     311             :  *
     312             :  * This is the same as the C function GDALGroupDeleteAttribute() or
     313             :  * GDALMDArrayDeleteAttribute()
     314             :  *
     315             :  * @param osName Attribute name.
     316             :  * @param papszOptions Driver specific options determining how the attribute.
     317             :  * should be deleted.
     318             :  *
     319             :  * @return true in case of success
     320             :  * @since GDAL 3.8
     321             :  */
     322           0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
     323             :                                         CPL_UNUSED CSLConstList papszOptions)
     324             : {
     325           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     326             :              "DeleteAttribute() not implemented");
     327           0 :     return false;
     328             : }
     329             : 
     330             : /************************************************************************/
     331             : /*                            GDALGroup()                               */
     332             : /************************************************************************/
     333             : 
     334             : //! @cond Doxygen_Suppress
     335        6916 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
     336        6916 :                      const std::string &osContext)
     337        6916 :     : m_osName(osParentName.empty() ? "/" : osName),
     338             :       m_osFullName(
     339       13832 :           !osParentName.empty()
     340       10709 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
     341             :               : "/"),
     342       17625 :       m_osContext(osContext)
     343             : {
     344        6916 : }
     345             : 
     346             : //! @endcond
     347             : 
     348             : /************************************************************************/
     349             : /*                            ~GDALGroup()                              */
     350             : /************************************************************************/
     351             : 
     352             : GDALGroup::~GDALGroup() = default;
     353             : 
     354             : /************************************************************************/
     355             : /*                          GetMDArrayNames()                           */
     356             : /************************************************************************/
     357             : 
     358             : /** Return the list of multidimensional array names contained in this group.
     359             :  *
     360             :  * @note Driver implementation: optionally implemented. If implemented,
     361             :  * OpenMDArray() should also be implemented.
     362             :  *
     363             :  * Drivers known to implement it: MEM, netCDF.
     364             :  *
     365             :  * This is the same as the C function GDALGroupGetMDArrayNames().
     366             :  *
     367             :  * @param papszOptions Driver specific options determining how arrays
     368             :  * should be retrieved. Pass nullptr for default behavior.
     369             :  *
     370             :  * @return the array names.
     371             :  */
     372             : std::vector<std::string>
     373           0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
     374             : {
     375           0 :     return {};
     376             : }
     377             : 
     378             : /************************************************************************/
     379             : /*                     GetMDArrayFullNamesRecursive()                   */
     380             : /************************************************************************/
     381             : 
     382             : /** Return the list of multidimensional array full names contained in this
     383             :  * group and its subgroups.
     384             :  *
     385             :  * This is the same as the C function GDALGroupGetMDArrayFullNamesRecursive().
     386             :  *
     387             :  * @param papszGroupOptions Driver specific options determining how groups
     388             :  * should be retrieved. Pass nullptr for default behavior.
     389             :  * @param papszArrayOptions Driver specific options determining how arrays
     390             :  * should be retrieved. Pass nullptr for default behavior.
     391             :  *
     392             :  * @return the array full names.
     393             :  *
     394             :  * @since 3.11
     395             :  */
     396             : std::vector<std::string>
     397           3 : GDALGroup::GetMDArrayFullNamesRecursive(CSLConstList papszGroupOptions,
     398             :                                         CSLConstList papszArrayOptions) const
     399             : {
     400           3 :     std::vector<std::string> ret;
     401           6 :     std::list<std::shared_ptr<GDALGroup>> stackGroups;
     402           3 :     stackGroups.push_back(nullptr);  // nullptr means this
     403           9 :     while (!stackGroups.empty())
     404             :     {
     405          12 :         std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
     406           6 :         stackGroups.erase(stackGroups.begin());
     407           6 :         const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
     408          10 :         for (const std::string &arrayName :
     409          26 :              poCurGroup->GetMDArrayNames(papszArrayOptions))
     410             :         {
     411          20 :             std::string osFullName = poCurGroup->GetFullName();
     412          10 :             if (!osFullName.empty() && osFullName.back() != '/')
     413           3 :                 osFullName += '/';
     414          10 :             osFullName += arrayName;
     415          10 :             ret.push_back(std::move(osFullName));
     416             :         }
     417           6 :         auto insertionPoint = stackGroups.begin();
     418           3 :         for (const auto &osSubGroup :
     419          12 :              poCurGroup->GetGroupNames(papszGroupOptions))
     420             :         {
     421           6 :             auto poSubGroup = poCurGroup->OpenGroup(osSubGroup);
     422           3 :             if (poSubGroup)
     423           3 :                 stackGroups.insert(insertionPoint, std::move(poSubGroup));
     424             :         }
     425             :     }
     426             : 
     427           6 :     return ret;
     428             : }
     429             : 
     430             : /************************************************************************/
     431             : /*                            OpenMDArray()                             */
     432             : /************************************************************************/
     433             : 
     434             : /** Open and return a multidimensional array.
     435             :  *
     436             :  * @note Driver implementation: optionally implemented. If implemented,
     437             :  * GetMDArrayNames() should also be implemented.
     438             :  *
     439             :  * Drivers known to implement it: MEM, netCDF.
     440             :  *
     441             :  * This is the same as the C function GDALGroupOpenMDArray().
     442             :  *
     443             :  * @param osName Array name.
     444             :  * @param papszOptions Driver specific options determining how the array should
     445             :  * be opened.  Pass nullptr for default behavior.
     446             :  *
     447             :  * @return the array, or nullptr.
     448             :  */
     449             : std::shared_ptr<GDALMDArray>
     450           0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
     451             :                        CPL_UNUSED CSLConstList papszOptions) const
     452             : {
     453           0 :     return nullptr;
     454             : }
     455             : 
     456             : /************************************************************************/
     457             : /*                           GetGroupNames()                            */
     458             : /************************************************************************/
     459             : 
     460             : /** Return the list of sub-groups contained in this group.
     461             :  *
     462             :  * @note Driver implementation: optionally implemented. If implemented,
     463             :  * OpenGroup() should also be implemented.
     464             :  *
     465             :  * Drivers known to implement it: MEM, netCDF.
     466             :  *
     467             :  * This is the same as the C function GDALGroupGetGroupNames().
     468             :  *
     469             :  * @param papszOptions Driver specific options determining how groups
     470             :  * should be retrieved. Pass nullptr for default behavior.
     471             :  *
     472             :  * @return the group names.
     473             :  */
     474             : std::vector<std::string>
     475           4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
     476             : {
     477           4 :     return {};
     478             : }
     479             : 
     480             : /************************************************************************/
     481             : /*                             OpenGroup()                              */
     482             : /************************************************************************/
     483             : 
     484             : /** Open and return a sub-group.
     485             :  *
     486             :  * @note Driver implementation: optionally implemented. If implemented,
     487             :  * GetGroupNames() should also be implemented.
     488             :  *
     489             :  * Drivers known to implement it: MEM, netCDF.
     490             :  *
     491             :  * This is the same as the C function GDALGroupOpenGroup().
     492             :  *
     493             :  * @param osName Sub-group name.
     494             :  * @param papszOptions Driver specific options determining how the sub-group
     495             :  * should be opened.  Pass nullptr for default behavior.
     496             :  *
     497             :  * @return the group, or nullptr.
     498             :  */
     499             : std::shared_ptr<GDALGroup>
     500           4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
     501             :                      CPL_UNUSED CSLConstList papszOptions) const
     502             : {
     503           4 :     return nullptr;
     504             : }
     505             : 
     506             : /************************************************************************/
     507             : /*                        GetVectorLayerNames()                         */
     508             : /************************************************************************/
     509             : 
     510             : /** Return the list of layer names contained in this group.
     511             :  *
     512             :  * @note Driver implementation: optionally implemented. If implemented,
     513             :  * OpenVectorLayer() should also be implemented.
     514             :  *
     515             :  * Drivers known to implement it: OpenFileGDB, FileGDB
     516             :  *
     517             :  * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
     518             :  * GDALDataset::GetLayer() should then be used.
     519             :  *
     520             :  * This is the same as the C function GDALGroupGetVectorLayerNames().
     521             :  *
     522             :  * @param papszOptions Driver specific options determining how layers
     523             :  * should be retrieved. Pass nullptr for default behavior.
     524             :  *
     525             :  * @return the vector layer names.
     526             :  * @since GDAL 3.4
     527             :  */
     528             : std::vector<std::string>
     529           1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
     530             : {
     531           1 :     return {};
     532             : }
     533             : 
     534             : /************************************************************************/
     535             : /*                           OpenVectorLayer()                          */
     536             : /************************************************************************/
     537             : 
     538             : /** Open and return a vector layer.
     539             :  *
     540             :  * Due to the historical ownership of OGRLayer* by GDALDataset*, the
     541             :  * lifetime of the returned OGRLayer* is linked to the one of the owner
     542             :  * dataset (contrary to the general design of this class where objects can be
     543             :  * used independently of the object that returned them)
     544             :  *
     545             :  * @note Driver implementation: optionally implemented. If implemented,
     546             :  * GetVectorLayerNames() should also be implemented.
     547             :  *
     548             :  * Drivers known to implement it: MEM, netCDF.
     549             :  *
     550             :  * This is the same as the C function GDALGroupOpenVectorLayer().
     551             :  *
     552             :  * @param osName Vector layer name.
     553             :  * @param papszOptions Driver specific options determining how the layer should
     554             :  * be opened.  Pass nullptr for default behavior.
     555             :  *
     556             :  * @return the group, or nullptr.
     557             :  */
     558           2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
     559             :                                      CPL_UNUSED CSLConstList papszOptions) const
     560             : {
     561           2 :     return nullptr;
     562             : }
     563             : 
     564             : /************************************************************************/
     565             : /*                             GetDimensions()                          */
     566             : /************************************************************************/
     567             : 
     568             : /** Return the list of dimensions contained in this group and used by its
     569             :  * arrays.
     570             :  *
     571             :  * This is for dimensions that can potentially be used by several arrays.
     572             :  * Not all drivers might implement this. To retrieve the dimensions used by
     573             :  * a specific array, use GDALMDArray::GetDimensions().
     574             :  *
     575             :  * Drivers known to implement it: MEM, netCDF
     576             :  *
     577             :  * This is the same as the C function GDALGroupGetDimensions().
     578             :  *
     579             :  * @param papszOptions Driver specific options determining how groups
     580             :  * should be retrieved. Pass nullptr for default behavior.
     581             :  *
     582             :  * @return the dimensions.
     583             :  */
     584             : std::vector<std::shared_ptr<GDALDimension>>
     585          11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
     586             : {
     587          11 :     return {};
     588             : }
     589             : 
     590             : /************************************************************************/
     591             : /*                         GetStructuralInfo()                          */
     592             : /************************************************************************/
     593             : 
     594             : /** Return structural information on the group.
     595             :  *
     596             :  * This may be the compression, etc..
     597             :  *
     598             :  * The return value should not be freed and is valid until GDALGroup is
     599             :  * released or this function called again.
     600             :  *
     601             :  * This is the same as the C function GDALGroupGetStructuralInfo().
     602             :  */
     603          41 : CSLConstList GDALGroup::GetStructuralInfo() const
     604             : {
     605          41 :     return nullptr;
     606             : }
     607             : 
     608             : /************************************************************************/
     609             : /*                              CreateGroup()                           */
     610             : /************************************************************************/
     611             : 
     612             : /** Create a sub-group within a group.
     613             :  *
     614             :  * Optionally implemented by drivers.
     615             :  *
     616             :  * Drivers known to implement it: MEM, netCDF
     617             :  *
     618             :  * This is the same as the C function GDALGroupCreateGroup().
     619             :  *
     620             :  * @param osName Sub-group name.
     621             :  * @param papszOptions Driver specific options determining how the sub-group
     622             :  * should be created.
     623             :  *
     624             :  * @return the new sub-group, or nullptr in case of error.
     625             :  */
     626             : std::shared_ptr<GDALGroup>
     627           0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
     628             :                        CPL_UNUSED CSLConstList papszOptions)
     629             : {
     630           0 :     CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
     631           0 :     return nullptr;
     632             : }
     633             : 
     634             : /************************************************************************/
     635             : /*                          DeleteGroup()                               */
     636             : /************************************************************************/
     637             : 
     638             : /** Delete a sub-group from a group.
     639             :  *
     640             :  * Optionally implemented.
     641             :  *
     642             :  * After this call, if a previously obtained instance of the deleted object
     643             :  * is still alive, no method other than for freeing it should be invoked.
     644             :  *
     645             :  * Drivers known to implement it: MEM, Zarr
     646             :  *
     647             :  * This is the same as the C function GDALGroupDeleteGroup().
     648             :  *
     649             :  * @param osName Sub-group name.
     650             :  * @param papszOptions Driver specific options determining how the group.
     651             :  * should be deleted.
     652             :  *
     653             :  * @return true in case of success
     654             :  * @since GDAL 3.8
     655             :  */
     656           0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
     657             :                             CPL_UNUSED CSLConstList papszOptions)
     658             : {
     659           0 :     CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
     660           0 :     return false;
     661             : }
     662             : 
     663             : /************************************************************************/
     664             : /*                            CreateDimension()                         */
     665             : /************************************************************************/
     666             : 
     667             : /** Create a dimension within a group.
     668             :  *
     669             :  * @note Driver implementation: drivers supporting CreateDimension() should
     670             :  * implement this method, but do not have necessarily to implement
     671             :  * GDALGroup::GetDimensions().
     672             :  *
     673             :  * Drivers known to implement it: MEM, netCDF
     674             :  *
     675             :  * This is the same as the C function GDALGroupCreateDimension().
     676             :  *
     677             :  * @param osName Dimension name.
     678             :  * @param osType Dimension type (might be empty, and ignored by drivers)
     679             :  * @param osDirection Dimension direction (might be empty, and ignored by
     680             :  * drivers)
     681             :  * @param nSize  Number of values indexed by this dimension. Should be > 0.
     682             :  * @param papszOptions Driver specific options determining how the dimension
     683             :  * should be created.
     684             :  *
     685             :  * @return the new dimension, or nullptr if case of error
     686             :  */
     687           0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
     688             :     CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
     689             :     CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
     690             :     CPL_UNUSED CSLConstList papszOptions)
     691             : {
     692           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     693             :              "CreateDimension() not implemented");
     694           0 :     return nullptr;
     695             : }
     696             : 
     697             : /************************************************************************/
     698             : /*                             CreateMDArray()                          */
     699             : /************************************************************************/
     700             : 
     701             : /** Create a multidimensional array within a group.
     702             :  *
     703             :  * It is recommended that the GDALDimension objects passed in aoDimensions
     704             :  * belong to this group, either by retrieving them with GetDimensions()
     705             :  * or creating a new one with CreateDimension().
     706             :  *
     707             :  * Optionally implemented.
     708             :  *
     709             :  * Drivers known to implement it: MEM, netCDF
     710             :  *
     711             :  * This is the same as the C function GDALGroupCreateMDArray().
     712             :  *
     713             :  * @note Driver implementation: drivers should take into account the possibility
     714             :  * that GDALDimension object passed in aoDimensions might belong to a different
     715             :  * group / dataset / driver and act accordingly.
     716             :  *
     717             :  * @param osName Array name.
     718             :  * @param aoDimensions List of dimensions, ordered from the slowest varying
     719             :  *                     dimension first to the fastest varying dimension last.
     720             :  *                     Might be empty for a scalar array (if supported by
     721             :  * driver)
     722             :  * @param oDataType  Array data type.
     723             :  * @param papszOptions Driver specific options determining how the array
     724             :  * should be created.
     725             :  *
     726             :  * @return the new array, or nullptr in case of error
     727             :  */
     728           0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
     729             :     CPL_UNUSED const std::string &osName,
     730             :     CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
     731             :     CPL_UNUSED const GDALExtendedDataType &oDataType,
     732             :     CPL_UNUSED CSLConstList papszOptions)
     733             : {
     734           0 :     CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
     735           0 :     return nullptr;
     736             : }
     737             : 
     738             : /************************************************************************/
     739             : /*                          DeleteMDArray()                             */
     740             : /************************************************************************/
     741             : 
     742             : /** Delete an array from a group.
     743             :  *
     744             :  * Optionally implemented.
     745             :  *
     746             :  * After this call, if a previously obtained instance of the deleted object
     747             :  * is still alive, no method other than for freeing it should be invoked.
     748             :  *
     749             :  * Drivers known to implement it: MEM, Zarr
     750             :  *
     751             :  * This is the same as the C function GDALGroupDeleteMDArray().
     752             :  *
     753             :  * @param osName Arrayname.
     754             :  * @param papszOptions Driver specific options determining how the array.
     755             :  * should be deleted.
     756             :  *
     757             :  * @return true in case of success
     758             :  * @since GDAL 3.8
     759             :  */
     760           0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
     761             :                               CPL_UNUSED CSLConstList papszOptions)
     762             : {
     763           0 :     CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
     764           0 :     return false;
     765             : }
     766             : 
     767             : /************************************************************************/
     768             : /*                           GetTotalCopyCost()                         */
     769             : /************************************************************************/
     770             : 
     771             : /** Return a total "cost" to copy the group.
     772             :  *
     773             :  * Used as a parameter for CopFrom()
     774             :  */
     775          25 : GUInt64 GDALGroup::GetTotalCopyCost() const
     776             : {
     777          25 :     GUInt64 nCost = COPY_COST;
     778          25 :     nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
     779             : 
     780          50 :     auto groupNames = GetGroupNames();
     781          31 :     for (const auto &name : groupNames)
     782             :     {
     783          12 :         auto subGroup = OpenGroup(name);
     784           6 :         if (subGroup)
     785             :         {
     786           6 :             nCost += subGroup->GetTotalCopyCost();
     787             :         }
     788             :     }
     789             : 
     790          25 :     auto arrayNames = GetMDArrayNames();
     791          71 :     for (const auto &name : arrayNames)
     792             :     {
     793          92 :         auto array = OpenMDArray(name);
     794          46 :         if (array)
     795             :         {
     796          46 :             nCost += array->GetTotalCopyCost();
     797             :         }
     798             :     }
     799          50 :     return nCost;
     800             : }
     801             : 
     802             : /************************************************************************/
     803             : /*                               CopyFrom()                             */
     804             : /************************************************************************/
     805             : 
     806             : /** Copy the content of a group into a new (generally empty) group.
     807             :  *
     808             :  * @param poDstRootGroup Destination root group. Must NOT be nullptr.
     809             :  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
     810             :  *                   of some output drivers this is not recommended)
     811             :  * @param poSrcGroup Source group. Must NOT be nullptr.
     812             :  * @param bStrict Whether to enable strict mode. In strict mode, any error will
     813             :  *                stop the copy. In relaxed mode, the copy will be attempted to
     814             :  *                be pursued.
     815             :  * @param nCurCost  Should be provided as a variable initially set to 0.
     816             :  * @param nTotalCost Total cost from GetTotalCopyCost().
     817             :  * @param pfnProgress Progress callback, or nullptr.
     818             :  * @param pProgressData Progress user data, or nulptr.
     819             :  * @param papszOptions Creation options. Currently, only array creation
     820             :  *                     options are supported. They must be prefixed with
     821             :  * "ARRAY:" . The scope may be further restricted to arrays of a certain
     822             :  *                     dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
     823             :  *                     For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
     824             :  *                     restrict BLOCKSIZE=256,256 to arrays of dimension 2.
     825             :  *                     Restriction to arrays of a given name is done with adding
     826             :  *                     "IF(NAME={name}):" after "ARRAY:". {name} can also be
     827             :  *                     a full qualified name.
     828             :  *                     A non-driver specific ARRAY option, "AUTOSCALE=YES" can
     829             :  * be used to ask (non indexing) variables of type Float32 or Float64 to be
     830             :  * scaled to UInt16 with scale and offset values being computed from the minimum
     831             :  * and maximum of the source array. The integer data type used can be set with
     832             :  *                     AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
     833             :  *
     834             :  * @return true in case of success (or partial success if bStrict == false).
     835             :  */
     836          25 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
     837             :                          GDALDataset *poSrcDS,
     838             :                          const std::shared_ptr<GDALGroup> &poSrcGroup,
     839             :                          bool bStrict, GUInt64 &nCurCost,
     840             :                          const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
     841             :                          void *pProgressData, CSLConstList papszOptions)
     842             : {
     843          25 :     if (pfnProgress == nullptr)
     844           0 :         pfnProgress = GDALDummyProgress;
     845             : 
     846             : #define EXIT_OR_CONTINUE_IF_NULL(x)                                            \
     847             :     if (!(x))                                                                  \
     848             :     {                                                                          \
     849             :         if (bStrict)                                                           \
     850             :             return false;                                                      \
     851             :         continue;                                                              \
     852             :     }                                                                          \
     853             :     (void)0
     854             : 
     855             :     try
     856             :     {
     857          25 :         nCurCost += GDALGroup::COPY_COST;
     858             : 
     859          50 :         const auto srcDims = poSrcGroup->GetDimensions();
     860             :         std::map<std::string, std::shared_ptr<GDALDimension>>
     861          50 :             mapExistingDstDims;
     862          50 :         std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
     863          63 :         for (const auto &dim : srcDims)
     864             :         {
     865             :             auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
     866          38 :                                           dim->GetDirection(), dim->GetSize());
     867          38 :             EXIT_OR_CONTINUE_IF_NULL(dstDim);
     868          38 :             mapExistingDstDims[dim->GetName()] = std::move(dstDim);
     869          76 :             auto poIndexingVarSrc(dim->GetIndexingVariable());
     870          38 :             if (poIndexingVarSrc)
     871             :             {
     872             :                 mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
     873          20 :                                                        ->GetName()] =
     874          40 :                     dim->GetName();
     875             :             }
     876             :         }
     877             : 
     878          50 :         auto attrs = poSrcGroup->GetAttributes();
     879          32 :         for (const auto &attr : attrs)
     880             :         {
     881             :             auto dstAttr =
     882           7 :                 CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
     883          14 :                                 attr->GetDataType());
     884           7 :             EXIT_OR_CONTINUE_IF_NULL(dstAttr);
     885           7 :             auto raw(attr->ReadAsRaw());
     886           7 :             if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
     887           0 :                 return false;
     888             :         }
     889          25 :         if (!attrs.empty())
     890             :         {
     891           5 :             nCurCost += attrs.size() * GDALAttribute::COPY_COST;
     892           5 :             if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
     893           0 :                 return false;
     894             :         }
     895             : 
     896             :         const auto CopyArray =
     897          46 :             [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
     898             :              &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
     899             :              papszOptions, bStrict, &nCurCost,
     900         417 :              nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
     901             :         {
     902             :             // Map source dimensions to target dimensions
     903          92 :             std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
     904          46 :             const auto &srcArrayDims(srcArray->GetDimensions());
     905         118 :             for (const auto &dim : srcArrayDims)
     906             :             {
     907             :                 auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
     908          72 :                     dim->GetFullName());
     909          72 :                 if (dstDim && dstDim->GetSize() == dim->GetSize())
     910             :                 {
     911          62 :                     dstArrayDims.emplace_back(dstDim);
     912             :                 }
     913             :                 else
     914             :                 {
     915          10 :                     auto oIter = mapExistingDstDims.find(dim->GetName());
     916          19 :                     if (oIter != mapExistingDstDims.end() &&
     917           9 :                         oIter->second->GetSize() == dim->GetSize())
     918             :                     {
     919           8 :                         dstArrayDims.emplace_back(oIter->second);
     920             :                     }
     921             :                     else
     922             :                     {
     923           2 :                         std::string newDimName;
     924           2 :                         if (oIter == mapExistingDstDims.end())
     925             :                         {
     926           1 :                             newDimName = dim->GetName();
     927             :                         }
     928             :                         else
     929             :                         {
     930           1 :                             std::string newDimNamePrefix(srcArray->GetName() +
     931           3 :                                                          '_' + dim->GetName());
     932           1 :                             newDimName = newDimNamePrefix;
     933           1 :                             int nIterCount = 2;
     934           0 :                             while (
     935           1 :                                 cpl::contains(mapExistingDstDims, newDimName))
     936             :                             {
     937           0 :                                 newDimName = newDimNamePrefix +
     938           0 :                                              CPLSPrintf("_%d", nIterCount);
     939           0 :                                 nIterCount++;
     940             :                             }
     941             :                         }
     942           4 :                         dstDim = CreateDimension(newDimName, dim->GetType(),
     943             :                                                  dim->GetDirection(),
     944           4 :                                                  dim->GetSize());
     945           2 :                         if (!dstDim)
     946           0 :                             return false;
     947           2 :                         mapExistingDstDims[newDimName] = dstDim;
     948           2 :                         dstArrayDims.emplace_back(dstDim);
     949             :                     }
     950             :                 }
     951             :             }
     952             : 
     953          92 :             CPLStringList aosArrayCO;
     954          46 :             bool bAutoScale = false;
     955          46 :             GDALDataType eAutoScaleType = GDT_UInt16;
     956          53 :             for (const char *pszItem : cpl::Iterate(papszOptions))
     957             :             {
     958           7 :                 if (STARTS_WITH_CI(pszItem, "ARRAY:"))
     959             :                 {
     960           7 :                     const char *pszOption = pszItem + strlen("ARRAY:");
     961           7 :                     if (STARTS_WITH_CI(pszOption, "IF(DIM="))
     962             :                     {
     963           1 :                         const char *pszNext = strchr(pszOption, ':');
     964           1 :                         if (pszNext != nullptr)
     965             :                         {
     966           1 :                             int nDim = atoi(pszOption + strlen("IF(DIM="));
     967           1 :                             if (static_cast<size_t>(nDim) ==
     968           1 :                                 dstArrayDims.size())
     969             :                             {
     970           1 :                                 pszOption = pszNext + 1;
     971             :                             }
     972             :                             else
     973             :                             {
     974           0 :                                 pszOption = nullptr;
     975             :                             }
     976             :                         }
     977             :                     }
     978           6 :                     else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
     979             :                     {
     980           2 :                         const char *pszName = pszOption + strlen("IF(NAME=");
     981           2 :                         const char *pszNext = strchr(pszName, ':');
     982           2 :                         if (pszNext != nullptr && pszNext > pszName &&
     983           2 :                             pszNext[-1] == ')')
     984             :                         {
     985           4 :                             CPLString osName;
     986           2 :                             osName.assign(pszName, pszNext - pszName - 1);
     987           3 :                             if (osName == srcArray->GetName() ||
     988           1 :                                 osName == srcArray->GetFullName())
     989             :                             {
     990           2 :                                 pszOption = pszNext + 1;
     991             :                             }
     992             :                             else
     993             :                             {
     994           0 :                                 pszOption = nullptr;
     995             :                             }
     996             :                         }
     997             :                     }
     998           7 :                     if (pszOption)
     999             :                     {
    1000           7 :                         if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
    1001             :                         {
    1002             :                             bAutoScale =
    1003           2 :                                 CPLTestBool(pszOption + strlen("AUTOSCALE="));
    1004             :                         }
    1005           5 :                         else if (STARTS_WITH_CI(pszOption,
    1006             :                                                 "AUTOSCALE_DATA_TYPE="))
    1007             :                         {
    1008           1 :                             const char *pszDataType =
    1009             :                                 pszOption + strlen("AUTOSCALE_DATA_TYPE=");
    1010           1 :                             eAutoScaleType = GDALGetDataTypeByName(pszDataType);
    1011           2 :                             if (GDALDataTypeIsComplex(eAutoScaleType) ||
    1012           1 :                                 GDALDataTypeIsFloating(eAutoScaleType))
    1013             :                             {
    1014           0 :                                 CPLError(CE_Failure, CPLE_NotSupported,
    1015             :                                          "Unsupported value for "
    1016             :                                          "AUTOSCALE_DATA_TYPE");
    1017           0 :                                 return false;
    1018             :                             }
    1019             :                         }
    1020             :                         else
    1021             :                         {
    1022           4 :                             aosArrayCO.AddString(pszOption);
    1023             :                         }
    1024             :                     }
    1025             :                 }
    1026             :             }
    1027             : 
    1028             :             auto oIterDimName =
    1029          46 :                 mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
    1030          46 :             const auto &srcArrayType = srcArray->GetDataType();
    1031             : 
    1032          46 :             std::shared_ptr<GDALMDArray> dstArray;
    1033             : 
    1034             :             // Only autoscale non-indexing variables
    1035          46 :             bool bHasOffset = false;
    1036          46 :             bool bHasScale = false;
    1037           4 :             if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
    1038           4 :                 (srcArrayType.GetNumericDataType() == GDT_Float16 ||
    1039           2 :                  srcArrayType.GetNumericDataType() == GDT_Float32 ||
    1040           0 :                  srcArrayType.GetNumericDataType() == GDT_Float64) &&
    1041           2 :                 srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
    1042          50 :                 srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
    1043          48 :                 oIterDimName == mapSrcVariableNameToIndexedDimName.end())
    1044             :             {
    1045           2 :                 constexpr bool bApproxOK = false;
    1046           2 :                 constexpr bool bForce = true;
    1047           2 :                 double dfMin = 0.0;
    1048           2 :                 double dfMax = 0.0;
    1049           2 :                 if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
    1050             :                                             nullptr, nullptr, nullptr, nullptr,
    1051           2 :                                             nullptr) != CE_None)
    1052             :                 {
    1053           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1054             :                              "Could not retrieve statistics for array %s",
    1055           0 :                              srcArray->GetName().c_str());
    1056           0 :                     return false;
    1057             :                 }
    1058           2 :                 double dfDTMin = 0;
    1059           2 :                 double dfDTMax = 0;
    1060             : #define setDTMinMax(ctype)                                                     \
    1061             :     do                                                                         \
    1062             :     {                                                                          \
    1063             :         dfDTMin = static_cast<double>(cpl::NumericLimits<ctype>::lowest());    \
    1064             :         dfDTMax = static_cast<double>(cpl::NumericLimits<ctype>::max());       \
    1065             :     } while (0)
    1066             : 
    1067           2 :                 switch (eAutoScaleType)
    1068             :                 {
    1069           0 :                     case GDT_Byte:
    1070           0 :                         setDTMinMax(GByte);
    1071           0 :                         break;
    1072           0 :                     case GDT_Int8:
    1073           0 :                         setDTMinMax(GInt8);
    1074           0 :                         break;
    1075           1 :                     case GDT_UInt16:
    1076           1 :                         setDTMinMax(GUInt16);
    1077           1 :                         break;
    1078           1 :                     case GDT_Int16:
    1079           1 :                         setDTMinMax(GInt16);
    1080           1 :                         break;
    1081           0 :                     case GDT_UInt32:
    1082           0 :                         setDTMinMax(GUInt32);
    1083           0 :                         break;
    1084           0 :                     case GDT_Int32:
    1085           0 :                         setDTMinMax(GInt32);
    1086           0 :                         break;
    1087           0 :                     case GDT_UInt64:
    1088           0 :                         setDTMinMax(std::uint64_t);
    1089           0 :                         break;
    1090           0 :                     case GDT_Int64:
    1091           0 :                         setDTMinMax(std::int64_t);
    1092           0 :                         break;
    1093           0 :                     case GDT_Float16:
    1094             :                     case GDT_Float32:
    1095             :                     case GDT_Float64:
    1096             :                     case GDT_Unknown:
    1097             :                     case GDT_CInt16:
    1098             :                     case GDT_CInt32:
    1099             :                     case GDT_CFloat16:
    1100             :                     case GDT_CFloat32:
    1101             :                     case GDT_CFloat64:
    1102             :                     case GDT_TypeCount:
    1103           0 :                         CPLAssert(false);
    1104             :                 }
    1105             : 
    1106             :                 dstArray =
    1107           4 :                     CreateMDArray(srcArray->GetName(), dstArrayDims,
    1108           4 :                                   GDALExtendedDataType::Create(eAutoScaleType),
    1109           4 :                                   aosArrayCO.List());
    1110           2 :                 if (!dstArray)
    1111           0 :                     return !bStrict;
    1112             : 
    1113           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1114             :                 {
    1115             :                     // If there's a nodata value in the source array, reserve
    1116             :                     // DTMax for that purpose in the target scaled array
    1117           1 :                     if (!dstArray->SetNoDataValue(dfDTMax))
    1118             :                     {
    1119           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    1120             :                                  "Cannot set nodata value");
    1121           0 :                         return false;
    1122             :                     }
    1123           1 :                     dfDTMax--;
    1124             :                 }
    1125           2 :                 const double dfScale =
    1126           2 :                     dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
    1127           2 :                 const double dfOffset = dfMin - dfDTMin * dfScale;
    1128             : 
    1129           4 :                 if (!dstArray->SetOffset(dfOffset) ||
    1130           2 :                     !dstArray->SetScale(dfScale))
    1131             :                 {
    1132           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1133             :                              "Cannot set scale/offset");
    1134           0 :                     return false;
    1135             :                 }
    1136             : 
    1137           2 :                 auto poUnscaled = dstArray->GetUnscaled();
    1138           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1139             :                 {
    1140           1 :                     poUnscaled->SetNoDataValue(
    1141             :                         srcArray->GetNoDataValueAsDouble());
    1142             :                 }
    1143             : 
    1144             :                 // Copy source array into unscaled array
    1145           4 :                 if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1146             :                                           nCurCost, nTotalCost, pfnProgress,
    1147           2 :                                           pProgressData))
    1148             :                 {
    1149           0 :                     return false;
    1150             :                 }
    1151             :             }
    1152             :             else
    1153             :             {
    1154          88 :                 dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
    1155          88 :                                          srcArrayType, aosArrayCO.List());
    1156          44 :                 if (!dstArray)
    1157           0 :                     return !bStrict;
    1158             : 
    1159          88 :                 if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1160             :                                         nCurCost, nTotalCost, pfnProgress,
    1161          44 :                                         pProgressData))
    1162             :                 {
    1163           0 :                     return false;
    1164             :                 }
    1165             :             }
    1166             : 
    1167             :             // If this array is the indexing variable of a dimension, link them
    1168             :             // together.
    1169          46 :             if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
    1170             :             {
    1171             :                 auto oCorrespondingDimIter =
    1172          20 :                     mapExistingDstDims.find(oIterDimName->second);
    1173          20 :                 if (oCorrespondingDimIter != mapExistingDstDims.end())
    1174             :                 {
    1175             :                     CPLErrorStateBackuper oErrorStateBackuper(
    1176          20 :                         CPLQuietErrorHandler);
    1177          40 :                     oCorrespondingDimIter->second->SetIndexingVariable(
    1178          20 :                         std::move(dstArray));
    1179             :                 }
    1180             :             }
    1181             : 
    1182          46 :             return true;
    1183          25 :         };
    1184             : 
    1185          50 :         const auto arrayNames = poSrcGroup->GetMDArrayNames();
    1186             : 
    1187             :         // Start by copying arrays that are indexing variables of dimensions
    1188          71 :         for (const auto &name : arrayNames)
    1189             :         {
    1190          46 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1191          46 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1192             : 
    1193          46 :             if (cpl::contains(mapSrcVariableNameToIndexedDimName,
    1194          46 :                               srcArray->GetName()))
    1195             :             {
    1196          20 :                 if (!CopyArray(srcArray))
    1197           0 :                     return false;
    1198             :             }
    1199             :         }
    1200             : 
    1201             :         // Then copy regular arrays
    1202          71 :         for (const auto &name : arrayNames)
    1203             :         {
    1204          46 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1205          46 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1206             : 
    1207          46 :             if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
    1208          46 :                                srcArray->GetName()))
    1209             :             {
    1210          26 :                 if (!CopyArray(srcArray))
    1211           0 :                     return false;
    1212             :             }
    1213             :         }
    1214             : 
    1215          50 :         const auto groupNames = poSrcGroup->GetGroupNames();
    1216          31 :         for (const auto &name : groupNames)
    1217             :         {
    1218           6 :             auto srcSubGroup = poSrcGroup->OpenGroup(name);
    1219           6 :             EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
    1220           6 :             auto dstSubGroup = CreateGroup(name);
    1221           6 :             EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
    1222          12 :             if (!dstSubGroup->CopyFrom(
    1223             :                     poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
    1224           6 :                     nTotalCost, pfnProgress, pProgressData, papszOptions))
    1225           0 :                 return false;
    1226             :         }
    1227             : 
    1228          25 :         if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    1229           0 :             return false;
    1230             : 
    1231          25 :         return true;
    1232             :     }
    1233           0 :     catch (const std::exception &e)
    1234             :     {
    1235           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1236           0 :         return false;
    1237             :     }
    1238             : }
    1239             : 
    1240             : /************************************************************************/
    1241             : /*                         GetInnerMostGroup()                          */
    1242             : /************************************************************************/
    1243             : 
    1244             : //! @cond Doxygen_Suppress
    1245             : const GDALGroup *
    1246        1275 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
    1247             :                              std::shared_ptr<GDALGroup> &curGroupHolder,
    1248             :                              std::string &osLastPart) const
    1249             : {
    1250        1275 :     if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
    1251           6 :         return nullptr;
    1252        1269 :     const GDALGroup *poCurGroup = this;
    1253             :     CPLStringList aosTokens(
    1254        2538 :         CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
    1255        1269 :     if (aosTokens.size() == 0)
    1256             :     {
    1257           0 :         return nullptr;
    1258             :     }
    1259             : 
    1260        1610 :     for (int i = 0; i < aosTokens.size() - 1; i++)
    1261             :     {
    1262         349 :         curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
    1263         349 :         if (!curGroupHolder)
    1264             :         {
    1265           8 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
    1266             :                      aosTokens[i]);
    1267           8 :             return nullptr;
    1268             :         }
    1269         341 :         poCurGroup = curGroupHolder.get();
    1270             :     }
    1271        1261 :     osLastPart = aosTokens[aosTokens.size() - 1];
    1272        1261 :     return poCurGroup;
    1273             : }
    1274             : 
    1275             : //! @endcond
    1276             : 
    1277             : /************************************************************************/
    1278             : /*                      OpenMDArrayFromFullname()                       */
    1279             : /************************************************************************/
    1280             : 
    1281             : /** Get an array from its fully qualified name */
    1282             : std::shared_ptr<GDALMDArray>
    1283         534 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
    1284             :                                    CSLConstList papszOptions) const
    1285             : {
    1286        1068 :     std::string osName;
    1287         534 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1288         534 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1289         534 :     if (poGroup == nullptr)
    1290          10 :         return nullptr;
    1291         524 :     return poGroup->OpenMDArray(osName, papszOptions);
    1292             : }
    1293             : 
    1294             : /************************************************************************/
    1295             : /*                          ResolveMDArray()                            */
    1296             : /************************************************************************/
    1297             : 
    1298             : /** Locate an array in a group and its subgroups by name.
    1299             :  *
    1300             :  * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
    1301             :  * used
    1302             :  * Otherwise the search will start from the group identified by osStartingPath,
    1303             :  * and an array whose name is osName will be looked for in this group (if
    1304             :  * osStartingPath is empty or "/", then the current group is used). If there
    1305             :  * is no match, then a recursive descendent search will be made in its
    1306             :  * subgroups. If there is no match in the subgroups, then the parent (if
    1307             :  * existing) of the group pointed by osStartingPath will be used as the new
    1308             :  * starting point for the search.
    1309             :  *
    1310             :  * @param osName name, qualified or not
    1311             :  * @param osStartingPath fully qualified name of the (sub-)group from which
    1312             :  *                       the search should be started. If this is a non-empty
    1313             :  *                       string, the group on which this method is called should
    1314             :  *                       nominally be the root group (otherwise the path will
    1315             :  *                       be interpreted as from the current group)
    1316             :  * @param papszOptions options to pass to OpenMDArray()
    1317             :  * @since GDAL 3.2
    1318             :  */
    1319             : std::shared_ptr<GDALMDArray>
    1320          19 : GDALGroup::ResolveMDArray(const std::string &osName,
    1321             :                           const std::string &osStartingPath,
    1322             :                           CSLConstList papszOptions) const
    1323             : {
    1324          19 :     if (!osName.empty() && osName[0] == '/')
    1325             :     {
    1326           1 :         auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
    1327           1 :         if (poArray)
    1328           1 :             return poArray;
    1329             :     }
    1330          36 :     std::string osPath(osStartingPath);
    1331          36 :     std::set<std::string> oSetAlreadyVisited;
    1332             : 
    1333             :     while (true)
    1334             :     {
    1335           0 :         std::shared_ptr<GDALGroup> curGroupHolder;
    1336           0 :         std::shared_ptr<GDALGroup> poGroup;
    1337             : 
    1338          22 :         std::queue<std::shared_ptr<GDALGroup>> oQueue;
    1339          22 :         bool goOn = false;
    1340          22 :         if (osPath.empty() || osPath == "/")
    1341             :         {
    1342          11 :             goOn = true;
    1343             :         }
    1344             :         else
    1345             :         {
    1346          22 :             std::string osLastPart;
    1347             :             const GDALGroup *poGroupPtr =
    1348          11 :                 GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
    1349          11 :             if (poGroupPtr)
    1350          11 :                 poGroup = poGroupPtr->OpenGroup(osLastPart);
    1351          22 :             if (poGroup &&
    1352          22 :                 !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
    1353             :             {
    1354          11 :                 oQueue.push(poGroup);
    1355          11 :                 goOn = true;
    1356             :             }
    1357             :         }
    1358             : 
    1359          22 :         if (goOn)
    1360             :         {
    1361          17 :             do
    1362             :             {
    1363             :                 const GDALGroup *groupPtr;
    1364          39 :                 if (!oQueue.empty())
    1365             :                 {
    1366          28 :                     poGroup = oQueue.front();
    1367          28 :                     oQueue.pop();
    1368          28 :                     groupPtr = poGroup.get();
    1369             :                 }
    1370             :                 else
    1371             :                 {
    1372          11 :                     groupPtr = this;
    1373             :                 }
    1374             : 
    1375          39 :                 auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
    1376          39 :                 if (poArray)
    1377          16 :                     return poArray;
    1378             : 
    1379          46 :                 const auto aosGroupNames = groupPtr->GetGroupNames();
    1380          47 :                 for (const auto &osGroupName : aosGroupNames)
    1381             :                 {
    1382          48 :                     auto poSubGroup = groupPtr->OpenGroup(osGroupName);
    1383          48 :                     if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
    1384          48 :                                                      poSubGroup->GetFullName()))
    1385             :                     {
    1386          24 :                         oQueue.push(poSubGroup);
    1387          24 :                         oSetAlreadyVisited.insert(poSubGroup->GetFullName());
    1388             :                     }
    1389             :                 }
    1390          23 :             } while (!oQueue.empty());
    1391             :         }
    1392             : 
    1393           6 :         if (osPath.empty() || osPath == "/")
    1394           2 :             break;
    1395             : 
    1396           4 :         const auto nPos = osPath.rfind('/');
    1397           4 :         if (nPos == 0)
    1398           1 :             osPath = "/";
    1399             :         else
    1400             :         {
    1401           3 :             if (nPos == std::string::npos)
    1402           0 :                 break;
    1403           3 :             osPath.resize(nPos);
    1404             :         }
    1405           4 :     }
    1406           2 :     return nullptr;
    1407             : }
    1408             : 
    1409             : /************************************************************************/
    1410             : /*                       OpenGroupFromFullname()                        */
    1411             : /************************************************************************/
    1412             : 
    1413             : /** Get a group from its fully qualified name.
    1414             :  * @since GDAL 3.2
    1415             :  */
    1416             : std::shared_ptr<GDALGroup>
    1417         563 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
    1418             :                                  CSLConstList papszOptions) const
    1419             : {
    1420        1126 :     std::string osName;
    1421         563 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1422         563 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1423         563 :     if (poGroup == nullptr)
    1424           2 :         return nullptr;
    1425         561 :     return poGroup->OpenGroup(osName, papszOptions);
    1426             : }
    1427             : 
    1428             : /************************************************************************/
    1429             : /*                      OpenDimensionFromFullname()                     */
    1430             : /************************************************************************/
    1431             : 
    1432             : /** Get a dimension from its fully qualified name */
    1433             : std::shared_ptr<GDALDimension>
    1434         167 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
    1435             : {
    1436         334 :     std::string osName;
    1437         167 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1438         167 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1439         167 :     if (poGroup == nullptr)
    1440           2 :         return nullptr;
    1441         330 :     auto dims(poGroup->GetDimensions());
    1442         286 :     for (auto &dim : dims)
    1443             :     {
    1444         241 :         if (dim->GetName() == osName)
    1445         120 :             return dim;
    1446             :     }
    1447          45 :     return nullptr;
    1448             : }
    1449             : 
    1450             : /************************************************************************/
    1451             : /*                           ClearStatistics()                          */
    1452             : /************************************************************************/
    1453             : 
    1454             : /**
    1455             :  * \brief Clear statistics.
    1456             :  *
    1457             :  * @since GDAL 3.4
    1458             :  */
    1459           0 : void GDALGroup::ClearStatistics()
    1460             : {
    1461           0 :     auto groupNames = GetGroupNames();
    1462           0 :     for (const auto &name : groupNames)
    1463             :     {
    1464           0 :         auto subGroup = OpenGroup(name);
    1465           0 :         if (subGroup)
    1466             :         {
    1467           0 :             subGroup->ClearStatistics();
    1468             :         }
    1469             :     }
    1470             : 
    1471           0 :     auto arrayNames = GetMDArrayNames();
    1472           0 :     for (const auto &name : arrayNames)
    1473             :     {
    1474           0 :         auto array = OpenMDArray(name);
    1475           0 :         if (array)
    1476             :         {
    1477           0 :             array->ClearStatistics();
    1478             :         }
    1479             :     }
    1480           0 : }
    1481             : 
    1482             : /************************************************************************/
    1483             : /*                            Rename()                                  */
    1484             : /************************************************************************/
    1485             : 
    1486             : /** Rename the group.
    1487             :  *
    1488             :  * This is not implemented by all drivers.
    1489             :  *
    1490             :  * Drivers known to implement it: MEM, netCDF, ZARR.
    1491             :  *
    1492             :  * This is the same as the C function GDALGroupRename().
    1493             :  *
    1494             :  * @param osNewName New name.
    1495             :  *
    1496             :  * @return true in case of success
    1497             :  * @since GDAL 3.8
    1498             :  */
    1499           0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
    1500             : {
    1501           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
    1502           0 :     return false;
    1503             : }
    1504             : 
    1505             : /************************************************************************/
    1506             : /*                         BaseRename()                                 */
    1507             : /************************************************************************/
    1508             : 
    1509             : //! @cond Doxygen_Suppress
    1510           8 : void GDALGroup::BaseRename(const std::string &osNewName)
    1511             : {
    1512           8 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
    1513           8 :     m_osFullName += osNewName;
    1514           8 :     m_osName = osNewName;
    1515             : 
    1516           8 :     NotifyChildrenOfRenaming();
    1517           8 : }
    1518             : 
    1519             : //! @endcond
    1520             : 
    1521             : /************************************************************************/
    1522             : /*                        ParentRenamed()                               */
    1523             : /************************************************************************/
    1524             : 
    1525             : //! @cond Doxygen_Suppress
    1526           7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
    1527             : {
    1528           7 :     m_osFullName = osNewParentFullName;
    1529           7 :     m_osFullName += "/";
    1530           7 :     m_osFullName += m_osName;
    1531             : 
    1532           7 :     NotifyChildrenOfRenaming();
    1533           7 : }
    1534             : 
    1535             : //! @endcond
    1536             : 
    1537             : /************************************************************************/
    1538             : /*                             Deleted()                                */
    1539             : /************************************************************************/
    1540             : 
    1541             : //! @cond Doxygen_Suppress
    1542          22 : void GDALGroup::Deleted()
    1543             : {
    1544          22 :     m_bValid = false;
    1545             : 
    1546          22 :     NotifyChildrenOfDeletion();
    1547          22 : }
    1548             : 
    1549             : //! @endcond
    1550             : 
    1551             : /************************************************************************/
    1552             : /*                        ParentDeleted()                               */
    1553             : /************************************************************************/
    1554             : 
    1555             : //! @cond Doxygen_Suppress
    1556           3 : void GDALGroup::ParentDeleted()
    1557             : {
    1558           3 :     Deleted();
    1559           3 : }
    1560             : 
    1561             : //! @endcond
    1562             : 
    1563             : /************************************************************************/
    1564             : /*                     CheckValidAndErrorOutIfNot()                     */
    1565             : /************************************************************************/
    1566             : 
    1567             : //! @cond Doxygen_Suppress
    1568       12469 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
    1569             : {
    1570       12469 :     if (!m_bValid)
    1571             :     {
    1572          14 :         CPLError(CE_Failure, CPLE_AppDefined,
    1573             :                  "This object has been deleted. No action on it is possible");
    1574             :     }
    1575       12469 :     return m_bValid;
    1576             : }
    1577             : 
    1578             : //! @endcond
    1579             : 
    1580             : /************************************************************************/
    1581             : /*                       ~GDALAbstractMDArray()                         */
    1582             : /************************************************************************/
    1583             : 
    1584             : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
    1585             : 
    1586             : /************************************************************************/
    1587             : /*                        GDALAbstractMDArray()                         */
    1588             : /************************************************************************/
    1589             : 
    1590             : //! @cond Doxygen_Suppress
    1591       20955 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
    1592       20955 :                                          const std::string &osName)
    1593             :     : m_osName(osName),
    1594             :       m_osFullName(
    1595       20955 :           !osParentName.empty()
    1596       40207 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
    1597       82117 :               : osName)
    1598             : {
    1599       20955 : }
    1600             : 
    1601             : //! @endcond
    1602             : 
    1603             : /************************************************************************/
    1604             : /*                           GetDimensions()                            */
    1605             : /************************************************************************/
    1606             : 
    1607             : /** \fn GDALAbstractMDArray::GetDimensions() const
    1608             :  * \brief Return the dimensions of an attribute/array.
    1609             :  *
    1610             :  * This is the same as the C functions GDALMDArrayGetDimensions() and
    1611             :  * similar to GDALAttributeGetDimensionsSize().
    1612             :  */
    1613             : 
    1614             : /************************************************************************/
    1615             : /*                           GetDataType()                              */
    1616             : /************************************************************************/
    1617             : 
    1618             : /** \fn GDALAbstractMDArray::GetDataType() const
    1619             :  * \brief Return the data type of an attribute/array.
    1620             :  *
    1621             :  * This is the same as the C functions GDALMDArrayGetDataType() and
    1622             :  * GDALAttributeGetDataType()
    1623             :  */
    1624             : 
    1625             : /************************************************************************/
    1626             : /*                        GetDimensionCount()                           */
    1627             : /************************************************************************/
    1628             : 
    1629             : /** Return the number of dimensions.
    1630             :  *
    1631             :  * Default implementation is GetDimensions().size(), and may be overridden by
    1632             :  * drivers if they have a faster / less expensive implementations.
    1633             :  *
    1634             :  * This is the same as the C function GDALMDArrayGetDimensionCount() or
    1635             :  * GDALAttributeGetDimensionCount().
    1636             :  *
    1637             :  */
    1638       23516 : size_t GDALAbstractMDArray::GetDimensionCount() const
    1639             : {
    1640       23516 :     return GetDimensions().size();
    1641             : }
    1642             : 
    1643             : /************************************************************************/
    1644             : /*                            Rename()                                  */
    1645             : /************************************************************************/
    1646             : 
    1647             : /** Rename the attribute/array.
    1648             :  *
    1649             :  * This is not implemented by all drivers.
    1650             :  *
    1651             :  * Drivers known to implement it: MEM, netCDF, Zarr.
    1652             :  *
    1653             :  * This is the same as the C functions GDALMDArrayRename() or
    1654             :  * GDALAttributeRename().
    1655             :  *
    1656             :  * @param osNewName New name.
    1657             :  *
    1658             :  * @return true in case of success
    1659             :  * @since GDAL 3.8
    1660             :  */
    1661           0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
    1662             : {
    1663           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
    1664           0 :     return false;
    1665             : }
    1666             : 
    1667             : /************************************************************************/
    1668             : /*                             CopyValue()                              */
    1669             : /************************************************************************/
    1670             : 
    1671             : /** Convert a value from a source type to a destination type.
    1672             :  *
    1673             :  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
    1674             :  * that must be freed with CPLFree().
    1675             :  */
    1676       80582 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
    1677             :                                      const GDALExtendedDataType &srcType,
    1678             :                                      void *pDst,
    1679             :                                      const GDALExtendedDataType &dstType)
    1680             : {
    1681      157248 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1682       76666 :         dstType.GetClass() == GEDTC_NUMERIC)
    1683             :     {
    1684       76419 :         GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
    1685             :                         dstType.GetNumericDataType(), 0, 1);
    1686       76419 :         return true;
    1687             :     }
    1688        7865 :     if (srcType.GetClass() == GEDTC_STRING &&
    1689        3702 :         dstType.GetClass() == GEDTC_STRING)
    1690             :     {
    1691             :         const char *srcStrPtr;
    1692        3269 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
    1693        3269 :         char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
    1694        3269 :         *reinterpret_cast<void **>(pDst) = pszDup;
    1695        3269 :         return true;
    1696             :     }
    1697        1141 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1698         247 :         dstType.GetClass() == GEDTC_STRING)
    1699             :     {
    1700         247 :         const char *str = nullptr;
    1701         247 :         switch (srcType.GetNumericDataType())
    1702             :         {
    1703           0 :             case GDT_Unknown:
    1704           0 :                 break;
    1705           0 :             case GDT_Byte:
    1706           0 :                 str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
    1707           0 :                 break;
    1708           3 :             case GDT_Int8:
    1709           3 :                 str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
    1710           3 :                 break;
    1711          48 :             case GDT_UInt16:
    1712          48 :                 str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
    1713          48 :                 break;
    1714           0 :             case GDT_Int16:
    1715           0 :                 str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
    1716           0 :                 break;
    1717           8 :             case GDT_UInt32:
    1718           8 :                 str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
    1719           8 :                 break;
    1720          60 :             case GDT_Int32:
    1721          60 :                 str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
    1722          60 :                 break;
    1723           0 :             case GDT_UInt64:
    1724             :                 str =
    1725           0 :                     CPLSPrintf(CPL_FRMT_GUIB,
    1726             :                                static_cast<GUIntBig>(
    1727             :                                    *static_cast<const std::uint64_t *>(pSrc)));
    1728           0 :                 break;
    1729          24 :             case GDT_Int64:
    1730          24 :                 str = CPLSPrintf(CPL_FRMT_GIB,
    1731             :                                  static_cast<GIntBig>(
    1732             :                                      *static_cast<const std::int64_t *>(pSrc)));
    1733          24 :                 break;
    1734           0 :             case GDT_Float16:
    1735           0 :                 str = CPLSPrintf("%.5g",
    1736             :                                  double(*static_cast<const GFloat16 *>(pSrc)));
    1737           0 :                 break;
    1738          17 :             case GDT_Float32:
    1739          17 :                 str = CPLSPrintf("%.9g", *static_cast<const float *>(pSrc));
    1740          17 :                 break;
    1741          85 :             case GDT_Float64:
    1742          85 :                 str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
    1743          85 :                 break;
    1744           2 :             case GDT_CInt16:
    1745             :             {
    1746           2 :                 const GInt16 *src = static_cast<const GInt16 *>(pSrc);
    1747           2 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
    1748           2 :                 break;
    1749             :             }
    1750           0 :             case GDT_CInt32:
    1751             :             {
    1752           0 :                 const GInt32 *src = static_cast<const GInt32 *>(pSrc);
    1753           0 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
    1754           0 :                 break;
    1755             :             }
    1756           0 :             case GDT_CFloat16:
    1757             :             {
    1758           0 :                 const GFloat16 *src = static_cast<const GFloat16 *>(pSrc);
    1759           0 :                 str = CPLSPrintf("%.5g+%.5gj", double(src[0]), double(src[1]));
    1760           0 :                 break;
    1761             :             }
    1762           0 :             case GDT_CFloat32:
    1763             :             {
    1764           0 :                 const float *src = static_cast<const float *>(pSrc);
    1765           0 :                 str = CPLSPrintf("%.9g+%.9gj", src[0], src[1]);
    1766           0 :                 break;
    1767             :             }
    1768           0 :             case GDT_CFloat64:
    1769             :             {
    1770           0 :                 const double *src = static_cast<const double *>(pSrc);
    1771           0 :                 str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
    1772           0 :                 break;
    1773             :             }
    1774           0 :             case GDT_TypeCount:
    1775           0 :                 CPLAssert(false);
    1776             :                 break;
    1777             :         }
    1778         247 :         char *pszDup = str ? CPLStrdup(str) : nullptr;
    1779         247 :         *reinterpret_cast<void **>(pDst) = pszDup;
    1780         247 :         return true;
    1781             :     }
    1782        1080 :     if (srcType.GetClass() == GEDTC_STRING &&
    1783         433 :         dstType.GetClass() == GEDTC_NUMERIC)
    1784             :     {
    1785             :         const char *srcStrPtr;
    1786         433 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
    1787         433 :         if (dstType.GetNumericDataType() == GDT_Int64)
    1788             :         {
    1789           2 :             *(static_cast<int64_t *>(pDst)) =
    1790           2 :                 srcStrPtr == nullptr ? 0
    1791           1 :                                      : static_cast<int64_t>(atoll(srcStrPtr));
    1792             :         }
    1793         431 :         else if (dstType.GetNumericDataType() == GDT_UInt64)
    1794             :         {
    1795           2 :             *(static_cast<uint64_t *>(pDst)) =
    1796           2 :                 srcStrPtr == nullptr
    1797           2 :                     ? 0
    1798           1 :                     : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
    1799             :         }
    1800             :         else
    1801             :         {
    1802             :             // FIXME GDT_UInt64
    1803         429 :             const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
    1804         429 :             GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
    1805             :                             dstType.GetNumericDataType(), 0, 1);
    1806             :         }
    1807         433 :         return true;
    1808             :     }
    1809         428 :     if (srcType.GetClass() == GEDTC_COMPOUND &&
    1810         214 :         dstType.GetClass() == GEDTC_COMPOUND)
    1811             :     {
    1812         214 :         const auto &srcComponents = srcType.GetComponents();
    1813         214 :         const auto &dstComponents = dstType.GetComponents();
    1814         214 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
    1815         214 :         GByte *pabyDst = static_cast<GByte *>(pDst);
    1816             : 
    1817             :         std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
    1818         428 :             srcComponentMap;
    1819        1025 :         for (const auto &srcComp : srcComponents)
    1820             :         {
    1821         811 :             srcComponentMap[srcComp->GetName()] = &srcComp;
    1822             :         }
    1823         582 :         for (const auto &dstComp : dstComponents)
    1824             :         {
    1825         368 :             auto oIter = srcComponentMap.find(dstComp->GetName());
    1826         368 :             if (oIter == srcComponentMap.end())
    1827           0 :                 return false;
    1828         368 :             const auto &srcComp = *(oIter->second);
    1829        1104 :             if (!GDALExtendedDataType::CopyValue(
    1830         368 :                     pabySrc + srcComp->GetOffset(), srcComp->GetType(),
    1831         368 :                     pabyDst + dstComp->GetOffset(), dstComp->GetType()))
    1832             :             {
    1833           0 :                 return false;
    1834             :             }
    1835             :         }
    1836         214 :         return true;
    1837             :     }
    1838             : 
    1839           0 :     return false;
    1840             : }
    1841             : 
    1842             : /************************************************************************/
    1843             : /*                             CopyValues()                             */
    1844             : /************************************************************************/
    1845             : 
    1846             : /** Convert severals value from a source type to a destination type.
    1847             :  *
    1848             :  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
    1849             :  * that must be freed with CPLFree().
    1850             :  */
    1851         365 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
    1852             :                                       const GDALExtendedDataType &srcType,
    1853             :                                       GPtrDiff_t nSrcStrideInElts, void *pDst,
    1854             :                                       const GDALExtendedDataType &dstType,
    1855             :                                       GPtrDiff_t nDstStrideInElts,
    1856             :                                       size_t nValues)
    1857             : {
    1858             :     const auto nSrcStrideInBytes =
    1859         365 :         nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
    1860             :     const auto nDstStrideInBytes =
    1861         365 :         nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
    1862         631 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1863         266 :         dstType.GetClass() == GEDTC_NUMERIC &&
    1864         266 :         nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
    1865         266 :         nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
    1866         897 :         nDstStrideInBytes >= std::numeric_limits<int>::min() &&
    1867         266 :         nDstStrideInBytes <= std::numeric_limits<int>::max())
    1868             :     {
    1869         266 :         GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
    1870             :                         static_cast<int>(nSrcStrideInBytes), pDst,
    1871             :                         dstType.GetNumericDataType(),
    1872             :                         static_cast<int>(nDstStrideInBytes), nValues);
    1873             :     }
    1874             :     else
    1875             :     {
    1876          99 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
    1877          99 :         GByte *pabyDst = static_cast<GByte *>(pDst);
    1878         198 :         for (size_t i = 0; i < nValues; ++i)
    1879             :         {
    1880          99 :             if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
    1881           0 :                 return false;
    1882          99 :             pabySrc += nSrcStrideInBytes;
    1883          99 :             pabyDst += nDstStrideInBytes;
    1884             :         }
    1885             :     }
    1886         365 :     return true;
    1887             : }
    1888             : 
    1889             : /************************************************************************/
    1890             : /*                       CheckReadWriteParams()                         */
    1891             : /************************************************************************/
    1892             : //! @cond Doxygen_Suppress
    1893        8186 : bool GDALAbstractMDArray::CheckReadWriteParams(
    1894             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
    1895             :     const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
    1896             :     const void *buffer, const void *buffer_alloc_start,
    1897             :     size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
    1898             :     std::vector<GPtrDiff_t> &tmp_bufferStride) const
    1899             : {
    1900           0 :     const auto lamda_error = []()
    1901             :     {
    1902           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1903             :                  "Not all elements pointed by buffer will fit in "
    1904             :                  "[buffer_alloc_start, "
    1905             :                  "buffer_alloc_start + buffer_alloc_size]");
    1906           0 :     };
    1907             : 
    1908        8186 :     const auto &dims = GetDimensions();
    1909        8186 :     if (dims.empty())
    1910             :     {
    1911        3147 :         if (buffer_alloc_start)
    1912             :         {
    1913        2770 :             const size_t elementSize = bufferDataType.GetSize();
    1914        2770 :             const GByte *paby_buffer = static_cast<const GByte *>(buffer);
    1915        2770 :             const GByte *paby_buffer_alloc_start =
    1916             :                 static_cast<const GByte *>(buffer_alloc_start);
    1917        2770 :             const GByte *paby_buffer_alloc_end =
    1918             :                 paby_buffer_alloc_start + buffer_alloc_size;
    1919             : 
    1920        2770 :             if (paby_buffer < paby_buffer_alloc_start ||
    1921        2770 :                 paby_buffer + elementSize > paby_buffer_alloc_end)
    1922             :             {
    1923           0 :                 lamda_error();
    1924           0 :                 return false;
    1925             :             }
    1926             :         }
    1927        3147 :         return true;
    1928             :     }
    1929             : 
    1930        5039 :     if (arrayStep == nullptr)
    1931             :     {
    1932        1346 :         tmp_arrayStep.resize(dims.size(), 1);
    1933        1346 :         arrayStep = tmp_arrayStep.data();
    1934             :     }
    1935       14141 :     for (size_t i = 0; i < dims.size(); i++)
    1936             :     {
    1937        9102 :         if (count[i] == 0)
    1938             :         {
    1939           0 :             CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
    1940             :                      static_cast<unsigned>(i));
    1941           0 :             return false;
    1942             :         }
    1943             :     }
    1944        5039 :     bool bufferStride_all_positive = true;
    1945        5039 :     if (bufferStride == nullptr)
    1946             :     {
    1947        1052 :         GPtrDiff_t stride = 1;
    1948             :         // To compute strides we must proceed from the fastest varying dimension
    1949             :         // (the last one), and then reverse the result
    1950        2397 :         for (size_t i = dims.size(); i != 0;)
    1951             :         {
    1952        1345 :             --i;
    1953        1345 :             tmp_bufferStride.push_back(stride);
    1954        1345 :             GUInt64 newStride = 0;
    1955             :             bool bOK;
    1956             :             try
    1957             :             {
    1958        1345 :                 newStride = (CPLSM(static_cast<uint64_t>(stride)) *
    1959        2690 :                              CPLSM(static_cast<uint64_t>(count[i])))
    1960        1345 :                                 .v();
    1961        1345 :                 bOK = static_cast<size_t>(newStride) == newStride &&
    1962        1345 :                       newStride < std::numeric_limits<size_t>::max() / 2;
    1963             :             }
    1964           0 :             catch (...)
    1965             :             {
    1966           0 :                 bOK = false;
    1967             :             }
    1968        1345 :             if (!bOK)
    1969             :             {
    1970           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
    1971           0 :                 return false;
    1972             :             }
    1973        1345 :             stride = static_cast<GPtrDiff_t>(newStride);
    1974             :         }
    1975        1052 :         std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
    1976        1052 :         bufferStride = tmp_bufferStride.data();
    1977             :     }
    1978             :     else
    1979             :     {
    1980       11742 :         for (size_t i = 0; i < dims.size(); i++)
    1981             :         {
    1982        7756 :             if (bufferStride[i] < 0)
    1983             :             {
    1984           1 :                 bufferStride_all_positive = false;
    1985           1 :                 break;
    1986             :             }
    1987             :         }
    1988             :     }
    1989       14112 :     for (size_t i = 0; i < dims.size(); i++)
    1990             :     {
    1991        9083 :         if (arrayStartIdx[i] >= dims[i]->GetSize())
    1992             :         {
    1993           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    1994             :                      "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
    1995             :                      static_cast<unsigned>(i),
    1996           2 :                      static_cast<GUInt64>(arrayStartIdx[i]),
    1997           2 :                      static_cast<GUInt64>(dims[i]->GetSize()));
    1998           2 :             return false;
    1999             :         }
    2000             :         bool bOverflow;
    2001        9081 :         if (arrayStep[i] >= 0)
    2002             :         {
    2003             :             try
    2004             :             {
    2005        8486 :                 bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
    2006        8488 :                              CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    2007       33947 :                                  CPLSM(static_cast<uint64_t>(arrayStep[i])))
    2008        8486 :                                 .v() >= dims[i]->GetSize();
    2009             :             }
    2010           1 :             catch (...)
    2011             :             {
    2012           1 :                 bOverflow = true;
    2013             :             }
    2014        8487 :             if (bOverflow)
    2015             :             {
    2016           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2017             :                          "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
    2018             :                          ">= " CPL_FRMT_GUIB,
    2019             :                          static_cast<unsigned>(i), static_cast<unsigned>(i),
    2020             :                          static_cast<unsigned>(i),
    2021           5 :                          static_cast<GUInt64>(dims[i]->GetSize()));
    2022           5 :                 return false;
    2023             :             }
    2024             :         }
    2025             :         else
    2026             :         {
    2027             :             try
    2028             :             {
    2029         594 :                 bOverflow =
    2030         594 :                     arrayStartIdx[i] <
    2031         594 :                     (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    2032        1188 :                      CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
    2033             :                                ? (static_cast<uint64_t>(1) << 63)
    2034        1188 :                                : static_cast<uint64_t>(-arrayStep[i])))
    2035         594 :                         .v();
    2036             :             }
    2037           0 :             catch (...)
    2038             :             {
    2039           0 :                 bOverflow = true;
    2040             :             }
    2041         594 :             if (bOverflow)
    2042             :             {
    2043           3 :                 CPLError(
    2044             :                     CE_Failure, CPLE_AppDefined,
    2045             :                     "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
    2046             :                     static_cast<unsigned>(i), static_cast<unsigned>(i),
    2047             :                     static_cast<unsigned>(i));
    2048           3 :                 return false;
    2049             :             }
    2050             :         }
    2051             :     }
    2052             : 
    2053        5029 :     if (buffer_alloc_start)
    2054             :     {
    2055        2585 :         const size_t elementSize = bufferDataType.GetSize();
    2056        2585 :         const GByte *paby_buffer = static_cast<const GByte *>(buffer);
    2057        2585 :         const GByte *paby_buffer_alloc_start =
    2058             :             static_cast<const GByte *>(buffer_alloc_start);
    2059        2585 :         const GByte *paby_buffer_alloc_end =
    2060             :             paby_buffer_alloc_start + buffer_alloc_size;
    2061        2585 :         if (bufferStride_all_positive)
    2062             :         {
    2063        2585 :             if (paby_buffer < paby_buffer_alloc_start)
    2064             :             {
    2065           0 :                 lamda_error();
    2066           0 :                 return false;
    2067             :             }
    2068        2585 :             GUInt64 nOffset = elementSize;
    2069        7418 :             for (size_t i = 0; i < dims.size(); i++)
    2070             :             {
    2071             :                 try
    2072             :                 {
    2073        4833 :                     nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
    2074        4833 :                                CPLSM(static_cast<uint64_t>(bufferStride[i])) *
    2075        9666 :                                    CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    2076       19332 :                                    CPLSM(static_cast<uint64_t>(elementSize)))
    2077        4833 :                                   .v();
    2078             :                 }
    2079           0 :                 catch (...)
    2080             :                 {
    2081           0 :                     lamda_error();
    2082           0 :                     return false;
    2083             :                 }
    2084             :             }
    2085             : #if SIZEOF_VOIDP == 4
    2086             :             if (static_cast<size_t>(nOffset) != nOffset)
    2087             :             {
    2088             :                 lamda_error();
    2089             :                 return false;
    2090             :             }
    2091             : #endif
    2092        2585 :             if (paby_buffer + nOffset > paby_buffer_alloc_end)
    2093             :             {
    2094           0 :                 lamda_error();
    2095           0 :                 return false;
    2096             :             }
    2097             :         }
    2098           0 :         else if (dims.size() < 31)
    2099             :         {
    2100             :             // Check all corners of the hypercube
    2101           0 :             const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
    2102           0 :             for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
    2103             :             {
    2104           0 :                 const GByte *paby = paby_buffer;
    2105           0 :                 for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
    2106             :                      i++)
    2107             :                 {
    2108           0 :                     if (iCornerCode & (1U << i))
    2109             :                     {
    2110             :                         // We should check for integer overflows
    2111           0 :                         paby += bufferStride[i] * (count[i] - 1) * elementSize;
    2112             :                     }
    2113             :                 }
    2114           0 :                 if (paby < paby_buffer_alloc_start ||
    2115           0 :                     paby + elementSize > paby_buffer_alloc_end)
    2116             :                 {
    2117           0 :                     lamda_error();
    2118           0 :                     return false;
    2119             :                 }
    2120             :             }
    2121             :         }
    2122             :     }
    2123             : 
    2124        5029 :     return true;
    2125             : }
    2126             : 
    2127             : //! @endcond
    2128             : 
    2129             : /************************************************************************/
    2130             : /*                               Read()                                 */
    2131             : /************************************************************************/
    2132             : 
    2133             : /** Read part or totality of a multidimensional array or attribute.
    2134             :  *
    2135             :  * This will extract the content of a hyper-rectangle from the array into
    2136             :  * a user supplied buffer.
    2137             :  *
    2138             :  * If bufferDataType is of type string, the values written in pDstBuffer
    2139             :  * will be char* pointers and the strings should be freed with CPLFree().
    2140             :  *
    2141             :  * This is the same as the C function GDALMDArrayRead().
    2142             :  *
    2143             :  * @param arrayStartIdx Values representing the starting index to read
    2144             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2145             :  *                      Array of GetDimensionCount() values. Must not be
    2146             :  *                      nullptr, unless for a zero-dimensional array.
    2147             :  *
    2148             :  * @param count         Values representing the number of values to extract in
    2149             :  *                      each dimension.
    2150             :  *                      Array of GetDimensionCount() values. Must not be
    2151             :  *                      nullptr, unless for a zero-dimensional array.
    2152             :  *
    2153             :  * @param arrayStep     Spacing between values to extract in each dimension.
    2154             :  *                      The spacing is in number of array elements, not bytes.
    2155             :  *                      If provided, must contain GetDimensionCount() values.
    2156             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
    2157             :  * default to indicate consecutive elements.
    2158             :  *
    2159             :  * @param bufferStride  Spacing between values to store in pDstBuffer.
    2160             :  *                      The spacing is in number of array elements, not bytes.
    2161             :  *                      If provided, must contain GetDimensionCount() values.
    2162             :  *                      Negative values are possible (for example to reorder
    2163             :  *                      from bottom-to-top to top-to-bottom).
    2164             :  *                      If set to nullptr, will be set so that pDstBuffer is
    2165             :  *                      written in a compact way, with elements of the last /
    2166             :  *                      fastest varying dimension being consecutive.
    2167             :  *
    2168             :  * @param bufferDataType Data type of values in pDstBuffer.
    2169             :  *
    2170             :  * @param pDstBuffer    User buffer to store the values read. Should be big
    2171             :  *                      enough to store the number of values indicated by
    2172             :  * count[] and with the spacing of bufferStride[].
    2173             :  *
    2174             :  * @param pDstBufferAllocStart Optional pointer that can be used to validate the
    2175             :  *                             validity of pDstBuffer. pDstBufferAllocStart
    2176             :  * should be the pointer returned by the malloc() or equivalent call used to
    2177             :  * allocate the buffer. It will generally be equal to pDstBuffer (when
    2178             :  * bufferStride[] values are all positive), but not necessarily. If specified,
    2179             :  * nDstBufferAllocSize should be also set to the appropriate value. If no
    2180             :  * validation is needed, nullptr can be passed.
    2181             :  *
    2182             :  * @param nDstBufferAllocSize  Optional buffer size, that can be used to
    2183             :  * validate the validity of pDstBuffer. This is the size of the buffer starting
    2184             :  * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
    2185             :  *                             set to the appropriate value.
    2186             :  *                             If no validation is needed, 0 can be passed.
    2187             :  *
    2188             :  * @return true in case of success.
    2189             :  */
    2190        2457 : bool GDALAbstractMDArray::Read(
    2191             :     const GUInt64 *arrayStartIdx, const size_t *count,
    2192             :     const GInt64 *arrayStep,         // step in elements
    2193             :     const GPtrDiff_t *bufferStride,  // stride in elements
    2194             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    2195             :     const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
    2196             : {
    2197        2457 :     if (!GetDataType().CanConvertTo(bufferDataType))
    2198             :     {
    2199           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2200             :                  "Array data type is not convertible to buffer data type");
    2201           0 :         return false;
    2202             :     }
    2203             : 
    2204        4914 :     std::vector<GInt64> tmp_arrayStep;
    2205        4914 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    2206        2457 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    2207             :                               bufferDataType, pDstBuffer, pDstBufferAllocStart,
    2208             :                               nDstBufferAllocSize, tmp_arrayStep,
    2209             :                               tmp_bufferStride))
    2210             :     {
    2211           0 :         return false;
    2212             :     }
    2213             : 
    2214        2457 :     return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
    2215        2457 :                  pDstBuffer);
    2216             : }
    2217             : 
    2218             : /************************************************************************/
    2219             : /*                                IWrite()                              */
    2220             : /************************************************************************/
    2221             : 
    2222             : //! @cond Doxygen_Suppress
    2223           1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
    2224             :                                  const GInt64 *, const GPtrDiff_t *,
    2225             :                                  const GDALExtendedDataType &, const void *)
    2226             : {
    2227           1 :     CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
    2228           1 :     return false;
    2229             : }
    2230             : 
    2231             : //! @endcond
    2232             : 
    2233             : /************************************************************************/
    2234             : /*                               Write()                                 */
    2235             : /************************************************************************/
    2236             : 
    2237             : /** Write part or totality of a multidimensional array or attribute.
    2238             :  *
    2239             :  * This will set the content of a hyper-rectangle into the array from
    2240             :  * a user supplied buffer.
    2241             :  *
    2242             :  * If bufferDataType is of type string, the values read from pSrcBuffer
    2243             :  * will be char* pointers.
    2244             :  *
    2245             :  * This is the same as the C function GDALMDArrayWrite().
    2246             :  *
    2247             :  * @param arrayStartIdx Values representing the starting index to write
    2248             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2249             :  *                      Array of GetDimensionCount() values. Must not be
    2250             :  *                      nullptr, unless for a zero-dimensional array.
    2251             :  *
    2252             :  * @param count         Values representing the number of values to write in
    2253             :  *                      each dimension.
    2254             :  *                      Array of GetDimensionCount() values. Must not be
    2255             :  *                      nullptr, unless for a zero-dimensional array.
    2256             :  *
    2257             :  * @param arrayStep     Spacing between values to write in each dimension.
    2258             :  *                      The spacing is in number of array elements, not bytes.
    2259             :  *                      If provided, must contain GetDimensionCount() values.
    2260             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
    2261             :  * default to indicate consecutive elements.
    2262             :  *
    2263             :  * @param bufferStride  Spacing between values to read from pSrcBuffer.
    2264             :  *                      The spacing is in number of array elements, not bytes.
    2265             :  *                      If provided, must contain GetDimensionCount() values.
    2266             :  *                      Negative values are possible (for example to reorder
    2267             :  *                      from bottom-to-top to top-to-bottom).
    2268             :  *                      If set to nullptr, will be set so that pSrcBuffer is
    2269             :  *                      written in a compact way, with elements of the last /
    2270             :  *                      fastest varying dimension being consecutive.
    2271             :  *
    2272             :  * @param bufferDataType Data type of values in pSrcBuffer.
    2273             :  *
    2274             :  * @param pSrcBuffer    User buffer to read the values from. Should be big
    2275             :  *                      enough to store the number of values indicated by
    2276             :  * count[] and with the spacing of bufferStride[].
    2277             :  *
    2278             :  * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
    2279             :  *                             validity of pSrcBuffer. pSrcBufferAllocStart
    2280             :  * should be the pointer returned by the malloc() or equivalent call used to
    2281             :  * allocate the buffer. It will generally be equal to pSrcBuffer (when
    2282             :  * bufferStride[] values are all positive), but not necessarily. If specified,
    2283             :  * nSrcBufferAllocSize should be also set to the appropriate value. If no
    2284             :  * validation is needed, nullptr can be passed.
    2285             :  *
    2286             :  * @param nSrcBufferAllocSize  Optional buffer size, that can be used to
    2287             :  * validate the validity of pSrcBuffer. This is the size of the buffer starting
    2288             :  * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
    2289             :  *                             set to the appropriate value.
    2290             :  *                             If no validation is needed, 0 can be passed.
    2291             :  *
    2292             :  * @return true in case of success.
    2293             :  */
    2294        1807 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
    2295             :                                 const size_t *count, const GInt64 *arrayStep,
    2296             :                                 const GPtrDiff_t *bufferStride,
    2297             :                                 const GDALExtendedDataType &bufferDataType,
    2298             :                                 const void *pSrcBuffer,
    2299             :                                 const void *pSrcBufferAllocStart,
    2300             :                                 size_t nSrcBufferAllocSize)
    2301             : {
    2302        1807 :     if (!bufferDataType.CanConvertTo(GetDataType()))
    2303             :     {
    2304           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2305             :                  "Buffer data type is not convertible to array data type");
    2306           0 :         return false;
    2307             :     }
    2308             : 
    2309        3614 :     std::vector<GInt64> tmp_arrayStep;
    2310        3614 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    2311        1807 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    2312             :                               bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
    2313             :                               nSrcBufferAllocSize, tmp_arrayStep,
    2314             :                               tmp_bufferStride))
    2315             :     {
    2316           0 :         return false;
    2317             :     }
    2318             : 
    2319        1807 :     return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
    2320        1807 :                   pSrcBuffer);
    2321             : }
    2322             : 
    2323             : /************************************************************************/
    2324             : /*                          GetTotalElementsCount()                     */
    2325             : /************************************************************************/
    2326             : 
    2327             : /** Return the total number of values in the array.
    2328             :  *
    2329             :  * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
    2330             :  * and GDALAttributeGetTotalElementsCount().
    2331             :  *
    2332             :  */
    2333        1114 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
    2334             : {
    2335        1114 :     const auto &dims = GetDimensions();
    2336        1114 :     if (dims.empty())
    2337         558 :         return 1;
    2338         556 :     GUInt64 nElts = 1;
    2339        1232 :     for (const auto &dim : dims)
    2340             :     {
    2341             :         try
    2342             :         {
    2343         676 :             nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
    2344        2028 :                      CPLSM(static_cast<uint64_t>(dim->GetSize())))
    2345         676 :                         .v();
    2346             :         }
    2347           0 :         catch (...)
    2348             :         {
    2349           0 :             return 0;
    2350             :         }
    2351             :     }
    2352         556 :     return nElts;
    2353             : }
    2354             : 
    2355             : /************************************************************************/
    2356             : /*                           GetBlockSize()                             */
    2357             : /************************************************************************/
    2358             : 
    2359             : /** Return the "natural" block size of the array along all dimensions.
    2360             :  *
    2361             :  * Some drivers might organize the array in tiles/blocks and reading/writing
    2362             :  * aligned on those tile/block boundaries will be more efficient.
    2363             :  *
    2364             :  * The returned number of elements in the vector is the same as
    2365             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
    2366             :  * the natural block size along the considered dimension.
    2367             :  * "Flat" arrays will typically return a vector of values set to 0.
    2368             :  *
    2369             :  * The default implementation will return a vector of values set to 0.
    2370             :  *
    2371             :  * This method is used by GetProcessingChunkSize().
    2372             :  *
    2373             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
    2374             :  * theoretical case of a 32-bit platform, this might exceed its size_t
    2375             :  * allocation capabilities.
    2376             :  *
    2377             :  * This is the same as the C function GDALMDArrayGetBlockSize().
    2378             :  *
    2379             :  * @return the block size, in number of elements along each dimension.
    2380             :  */
    2381         275 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
    2382             : {
    2383         275 :     return std::vector<GUInt64>(GetDimensionCount());
    2384             : }
    2385             : 
    2386             : /************************************************************************/
    2387             : /*                       GetProcessingChunkSize()                       */
    2388             : /************************************************************************/
    2389             : 
    2390             : /** \brief Return an optimal chunk size for read/write operations, given the
    2391             :  * natural block size and memory constraints specified.
    2392             :  *
    2393             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
    2394             :  * multiple of those returned by GetBlockSize() (unless the block define by
    2395             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
    2396             :  * returned by this method).
    2397             :  *
    2398             :  * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
    2399             :  *
    2400             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
    2401             :  * chunk.
    2402             :  *
    2403             :  * @return the chunk size, in number of elements along each dimension.
    2404             :  */
    2405             : std::vector<size_t>
    2406          70 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
    2407             : {
    2408          70 :     const auto &dims = GetDimensions();
    2409          70 :     const auto &nDTSize = GetDataType().GetSize();
    2410          70 :     std::vector<size_t> anChunkSize;
    2411         140 :     auto blockSize = GetBlockSize();
    2412          70 :     CPLAssert(blockSize.size() == dims.size());
    2413          70 :     size_t nChunkSize = nDTSize;
    2414          70 :     bool bOverflow = false;
    2415          70 :     constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
    2416             :     // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
    2417             :     // [1, min(sizet_max, dim_size[i])]
    2418             :     // Also make sure that the product of all anChunkSize[i]) fits on size_t
    2419         199 :     for (size_t i = 0; i < dims.size(); i++)
    2420             :     {
    2421             :         const auto sizeDimI =
    2422         258 :             std::max(static_cast<size_t>(1),
    2423         258 :                      static_cast<size_t>(
    2424         258 :                          std::min(static_cast<GUInt64>(kSIZE_T_MAX),
    2425         129 :                                   std::min(blockSize[i], dims[i]->GetSize()))));
    2426         129 :         anChunkSize.push_back(sizeDimI);
    2427         129 :         if (nChunkSize > kSIZE_T_MAX / sizeDimI)
    2428             :         {
    2429           4 :             bOverflow = true;
    2430             :         }
    2431             :         else
    2432             :         {
    2433         125 :             nChunkSize *= sizeDimI;
    2434             :         }
    2435             :     }
    2436          70 :     if (nChunkSize == 0)
    2437           0 :         return anChunkSize;
    2438             : 
    2439             :     // If the product of all anChunkSize[i] does not fit on size_t, then
    2440             :     // set lowest anChunkSize[i] to 1.
    2441          70 :     if (bOverflow)
    2442             :     {
    2443           2 :         nChunkSize = nDTSize;
    2444           2 :         bOverflow = false;
    2445           8 :         for (size_t i = dims.size(); i > 0;)
    2446             :         {
    2447           6 :             --i;
    2448           6 :             if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
    2449             :             {
    2450           4 :                 bOverflow = true;
    2451           4 :                 anChunkSize[i] = 1;
    2452             :             }
    2453             :             else
    2454             :             {
    2455           2 :                 nChunkSize *= anChunkSize[i];
    2456             :             }
    2457             :         }
    2458             :     }
    2459             : 
    2460          70 :     nChunkSize = nDTSize;
    2461         140 :     std::vector<size_t> anAccBlockSizeFromStart;
    2462         199 :     for (size_t i = 0; i < dims.size(); i++)
    2463             :     {
    2464         129 :         nChunkSize *= anChunkSize[i];
    2465         129 :         anAccBlockSizeFromStart.push_back(nChunkSize);
    2466             :     }
    2467          70 :     if (nChunkSize <= nMaxChunkMemory / 2)
    2468             :     {
    2469          66 :         size_t nVoxelsFromEnd = 1;
    2470         187 :         for (size_t i = dims.size(); i > 0;)
    2471             :         {
    2472         121 :             --i;
    2473             :             const auto nCurBlockSize =
    2474         121 :                 anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
    2475         121 :             const auto nMul = nMaxChunkMemory / nCurBlockSize;
    2476         121 :             if (nMul >= 2)
    2477             :             {
    2478         113 :                 const auto nSizeThisDim(dims[i]->GetSize());
    2479             :                 const auto nBlocksThisDim =
    2480         113 :                     DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
    2481         113 :                 anChunkSize[i] = static_cast<size_t>(std::min(
    2482         113 :                     anChunkSize[i] *
    2483         226 :                         std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
    2484         113 :                     nSizeThisDim));
    2485             :             }
    2486         121 :             nVoxelsFromEnd *= anChunkSize[i];
    2487             :         }
    2488             :     }
    2489          70 :     return anChunkSize;
    2490             : }
    2491             : 
    2492             : /************************************************************************/
    2493             : /*                         BaseRename()                                 */
    2494             : /************************************************************************/
    2495             : 
    2496             : //! @cond Doxygen_Suppress
    2497          18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
    2498             : {
    2499          18 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
    2500          18 :     m_osFullName += osNewName;
    2501          18 :     m_osName = osNewName;
    2502             : 
    2503          18 :     NotifyChildrenOfRenaming();
    2504          18 : }
    2505             : 
    2506             : //! @endcond
    2507             : 
    2508             : //! @cond Doxygen_Suppress
    2509             : /************************************************************************/
    2510             : /*                          ParentRenamed()                             */
    2511             : /************************************************************************/
    2512             : 
    2513          50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
    2514             : {
    2515          50 :     m_osFullName = osNewParentFullName;
    2516          50 :     m_osFullName += "/";
    2517          50 :     m_osFullName += m_osName;
    2518             : 
    2519          50 :     NotifyChildrenOfRenaming();
    2520          50 : }
    2521             : 
    2522             : //! @endcond
    2523             : 
    2524             : /************************************************************************/
    2525             : /*                             Deleted()                                */
    2526             : /************************************************************************/
    2527             : 
    2528             : //! @cond Doxygen_Suppress
    2529          52 : void GDALAbstractMDArray::Deleted()
    2530             : {
    2531          52 :     m_bValid = false;
    2532             : 
    2533          52 :     NotifyChildrenOfDeletion();
    2534          52 : }
    2535             : 
    2536             : //! @endcond
    2537             : 
    2538             : /************************************************************************/
    2539             : /*                        ParentDeleted()                               */
    2540             : /************************************************************************/
    2541             : 
    2542             : //! @cond Doxygen_Suppress
    2543          28 : void GDALAbstractMDArray::ParentDeleted()
    2544             : {
    2545          28 :     Deleted();
    2546          28 : }
    2547             : 
    2548             : //! @endcond
    2549             : 
    2550             : /************************************************************************/
    2551             : /*                     CheckValidAndErrorOutIfNot()                     */
    2552             : /************************************************************************/
    2553             : 
    2554             : //! @cond Doxygen_Suppress
    2555        5815 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
    2556             : {
    2557        5815 :     if (!m_bValid)
    2558             :     {
    2559          26 :         CPLError(CE_Failure, CPLE_AppDefined,
    2560             :                  "This object has been deleted. No action on it is possible");
    2561             :     }
    2562        5815 :     return m_bValid;
    2563             : }
    2564             : 
    2565             : //! @endcond
    2566             : 
    2567             : /************************************************************************/
    2568             : /*                             SetUnit()                                */
    2569             : /************************************************************************/
    2570             : 
    2571             : /** Set the variable unit.
    2572             :  *
    2573             :  * Values should conform as much as possible with those allowed by
    2574             :  * the NetCDF CF conventions:
    2575             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
    2576             :  * but others might be returned.
    2577             :  *
    2578             :  * Few examples are "meter", "degrees", "second", ...
    2579             :  * Empty value means unknown.
    2580             :  *
    2581             :  * This is the same as the C function GDALMDArraySetUnit()
    2582             :  *
    2583             :  * @note Driver implementation: optionally implemented.
    2584             :  *
    2585             :  * @param osUnit unit name.
    2586             :  * @return true in case of success.
    2587             :  */
    2588           0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
    2589             : {
    2590           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
    2591           0 :     return false;
    2592             : }
    2593             : 
    2594             : /************************************************************************/
    2595             : /*                             GetUnit()                                */
    2596             : /************************************************************************/
    2597             : 
    2598             : /** Return the array unit.
    2599             :  *
    2600             :  * Values should conform as much as possible with those allowed by
    2601             :  * the NetCDF CF conventions:
    2602             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
    2603             :  * but others might be returned.
    2604             :  *
    2605             :  * Few examples are "meter", "degrees", "second", ...
    2606             :  * Empty value means unknown.
    2607             :  *
    2608             :  * This is the same as the C function GDALMDArrayGetUnit()
    2609             :  */
    2610           5 : const std::string &GDALMDArray::GetUnit() const
    2611             : {
    2612           5 :     static const std::string emptyString;
    2613           5 :     return emptyString;
    2614             : }
    2615             : 
    2616             : /************************************************************************/
    2617             : /*                          SetSpatialRef()                             */
    2618             : /************************************************************************/
    2619             : 
    2620             : /** Assign a spatial reference system object to the array.
    2621             :  *
    2622             :  * This is the same as the C function GDALMDArraySetSpatialRef().
    2623             :  */
    2624           0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
    2625             : {
    2626           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
    2627           0 :     return false;
    2628             : }
    2629             : 
    2630             : /************************************************************************/
    2631             : /*                          GetSpatialRef()                             */
    2632             : /************************************************************************/
    2633             : 
    2634             : /** Return the spatial reference system object associated with the array.
    2635             :  *
    2636             :  * This is the same as the C function GDALMDArrayGetSpatialRef().
    2637             :  */
    2638           4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
    2639             : {
    2640           4 :     return nullptr;
    2641             : }
    2642             : 
    2643             : /************************************************************************/
    2644             : /*                        GetRawNoDataValue()                           */
    2645             : /************************************************************************/
    2646             : 
    2647             : /** Return the nodata value as a "raw" value.
    2648             :  *
    2649             :  * The value returned might be nullptr in case of no nodata value. When
    2650             :  * a nodata value is registered, a non-nullptr will be returned whose size in
    2651             :  * bytes is GetDataType().GetSize().
    2652             :  *
    2653             :  * The returned value should not be modified or freed. It is valid until
    2654             :  * the array is destroyed, or the next call to GetRawNoDataValue() or
    2655             :  * SetRawNoDataValue(), or any similar methods.
    2656             :  *
    2657             :  * @note Driver implementation: this method shall be implemented if nodata
    2658             :  * is supported.
    2659             :  *
    2660             :  * This is the same as the C function GDALMDArrayGetRawNoDataValue().
    2661             :  *
    2662             :  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
    2663             :  */
    2664           5 : const void *GDALMDArray::GetRawNoDataValue() const
    2665             : {
    2666           5 :     return nullptr;
    2667             : }
    2668             : 
    2669             : /************************************************************************/
    2670             : /*                        GetNoDataValueAsDouble()                      */
    2671             : /************************************************************************/
    2672             : 
    2673             : /** Return the nodata value as a double.
    2674             :  *
    2675             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
    2676             :  *
    2677             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2678             :  * a nodata value exists and can be converted to double. Might be nullptr.
    2679             :  *
    2680             :  * @return the nodata value as a double. A 0.0 value might also indicate the
    2681             :  * absence of a nodata value or an error in the conversion (*pbHasNoData will be
    2682             :  * set to false then).
    2683             :  */
    2684       22476 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
    2685             : {
    2686       22476 :     const void *pNoData = GetRawNoDataValue();
    2687       22476 :     double dfNoData = 0.0;
    2688       22476 :     const auto &eDT = GetDataType();
    2689       22476 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2690       22476 :     if (ok)
    2691             :     {
    2692       22186 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
    2693             :                         GDT_Float64, 0, 1);
    2694             :     }
    2695       22476 :     if (pbHasNoData)
    2696         437 :         *pbHasNoData = ok;
    2697       22476 :     return dfNoData;
    2698             : }
    2699             : 
    2700             : /************************************************************************/
    2701             : /*                        GetNoDataValueAsInt64()                       */
    2702             : /************************************************************************/
    2703             : 
    2704             : /** Return the nodata value as a Int64.
    2705             :  *
    2706             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2707             :  * a nodata value exists and can be converted to Int64. Might be nullptr.
    2708             :  *
    2709             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
    2710             :  *
    2711             :  * @return the nodata value as a Int64
    2712             :  *
    2713             :  * @since GDAL 3.5
    2714             :  */
    2715          12 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
    2716             : {
    2717          12 :     const void *pNoData = GetRawNoDataValue();
    2718          12 :     int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
    2719          12 :     const auto &eDT = GetDataType();
    2720          12 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2721          12 :     if (ok)
    2722             :     {
    2723           8 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
    2724             :                         GDT_Int64, 0, 1);
    2725             :     }
    2726          12 :     if (pbHasNoData)
    2727          12 :         *pbHasNoData = ok;
    2728          12 :     return nNoData;
    2729             : }
    2730             : 
    2731             : /************************************************************************/
    2732             : /*                       GetNoDataValueAsUInt64()                       */
    2733             : /************************************************************************/
    2734             : 
    2735             : /** Return the nodata value as a UInt64.
    2736             :  *
    2737             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
    2738             : 
    2739             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2740             :  * a nodata value exists and can be converted to UInt64. Might be nullptr.
    2741             :  *
    2742             :  * @return the nodata value as a UInt64
    2743             :  *
    2744             :  * @since GDAL 3.5
    2745             :  */
    2746           8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
    2747             : {
    2748           8 :     const void *pNoData = GetRawNoDataValue();
    2749           8 :     uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
    2750           8 :     const auto &eDT = GetDataType();
    2751           8 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2752           8 :     if (ok)
    2753             :     {
    2754           6 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
    2755             :                         GDT_UInt64, 0, 1);
    2756             :     }
    2757           8 :     if (pbHasNoData)
    2758           8 :         *pbHasNoData = ok;
    2759           8 :     return nNoData;
    2760             : }
    2761             : 
    2762             : /************************************************************************/
    2763             : /*                        SetRawNoDataValue()                           */
    2764             : /************************************************************************/
    2765             : 
    2766             : /** Set the nodata value as a "raw" value.
    2767             :  *
    2768             :  * The value passed might be nullptr in case of no nodata value. When
    2769             :  * a nodata value is registered, a non-nullptr whose size in
    2770             :  * bytes is GetDataType().GetSize() must be passed.
    2771             :  *
    2772             :  * This is the same as the C function GDALMDArraySetRawNoDataValue().
    2773             :  *
    2774             :  * @note Driver implementation: this method shall be implemented if setting
    2775             :  nodata
    2776             :  * is supported.
    2777             : 
    2778             :  * @return true in case of success.
    2779             :  */
    2780           0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
    2781             : {
    2782           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2783             :              "SetRawNoDataValue() not implemented");
    2784           0 :     return false;
    2785             : }
    2786             : 
    2787             : /************************************************************************/
    2788             : /*                           SetNoDataValue()                           */
    2789             : /************************************************************************/
    2790             : 
    2791             : /** Set the nodata value as a double.
    2792             :  *
    2793             :  * If the natural data type of the attribute/array is not double, type
    2794             :  * conversion will occur to the type returned by GetDataType().
    2795             :  *
    2796             :  * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
    2797             :  *
    2798             :  * @return true in case of success.
    2799             :  */
    2800          57 : bool GDALMDArray::SetNoDataValue(double dfNoData)
    2801             : {
    2802          57 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2803          57 :     bool bRet = false;
    2804          57 :     if (GDALExtendedDataType::CopyValue(
    2805         114 :             &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
    2806          57 :             GetDataType()))
    2807             :     {
    2808          57 :         bRet = SetRawNoDataValue(pRawNoData);
    2809             :     }
    2810          57 :     CPLFree(pRawNoData);
    2811          57 :     return bRet;
    2812             : }
    2813             : 
    2814             : /************************************************************************/
    2815             : /*                           SetNoDataValue()                           */
    2816             : /************************************************************************/
    2817             : 
    2818             : /** Set the nodata value as a Int64.
    2819             :  *
    2820             :  * If the natural data type of the attribute/array is not Int64, type conversion
    2821             :  * will occur to the type returned by GetDataType().
    2822             :  *
    2823             :  * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
    2824             :  *
    2825             :  * @return true in case of success.
    2826             :  *
    2827             :  * @since GDAL 3.5
    2828             :  */
    2829           3 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
    2830             : {
    2831           3 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2832           3 :     bool bRet = false;
    2833           3 :     if (GDALExtendedDataType::CopyValue(&nNoData,
    2834           6 :                                         GDALExtendedDataType::Create(GDT_Int64),
    2835           3 :                                         pRawNoData, GetDataType()))
    2836             :     {
    2837           3 :         bRet = SetRawNoDataValue(pRawNoData);
    2838             :     }
    2839           3 :     CPLFree(pRawNoData);
    2840           3 :     return bRet;
    2841             : }
    2842             : 
    2843             : /************************************************************************/
    2844             : /*                           SetNoDataValue()                           */
    2845             : /************************************************************************/
    2846             : 
    2847             : /** Set the nodata value as a Int64.
    2848             :  *
    2849             :  * If the natural data type of the attribute/array is not Int64, type conversion
    2850             :  * will occur to the type returned by GetDataType().
    2851             :  *
    2852             :  * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
    2853             :  *
    2854             :  * @return true in case of success.
    2855             :  *
    2856             :  * @since GDAL 3.5
    2857             :  */
    2858           1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
    2859             : {
    2860           1 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2861           1 :     bool bRet = false;
    2862           1 :     if (GDALExtendedDataType::CopyValue(
    2863           2 :             &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
    2864           1 :             GetDataType()))
    2865             :     {
    2866           1 :         bRet = SetRawNoDataValue(pRawNoData);
    2867             :     }
    2868           1 :     CPLFree(pRawNoData);
    2869           1 :     return bRet;
    2870             : }
    2871             : 
    2872             : /************************************************************************/
    2873             : /*                            Resize()                                  */
    2874             : /************************************************************************/
    2875             : 
    2876             : /** Resize an array to new dimensions.
    2877             :  *
    2878             :  * Not all drivers may allow this operation, and with restrictions (e.g.
    2879             :  * for netCDF, this is limited to growing of "unlimited" dimensions)
    2880             :  *
    2881             :  * Resizing a dimension used in other arrays will cause those other arrays
    2882             :  * to be resized.
    2883             :  *
    2884             :  * This is the same as the C function GDALMDArrayResize().
    2885             :  *
    2886             :  * @param anNewDimSizes Array of GetDimensionCount() values containing the
    2887             :  *                      new size of each indexing dimension.
    2888             :  * @param papszOptions Options. (Driver specific)
    2889             :  * @return true in case of success.
    2890             :  * @since GDAL 3.7
    2891             :  */
    2892           0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
    2893             :                          CPL_UNUSED CSLConstList papszOptions)
    2894             : {
    2895           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2896             :              "Resize() is not supported for this array");
    2897           0 :     return false;
    2898             : }
    2899             : 
    2900             : /************************************************************************/
    2901             : /*                               SetScale()                             */
    2902             : /************************************************************************/
    2903             : 
    2904             : /** Set the scale value to apply to raw values.
    2905             :  *
    2906             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2907             :  *
    2908             :  * This is the same as the C function GDALMDArraySetScale() /
    2909             :  * GDALMDArraySetScaleEx().
    2910             :  *
    2911             :  * @note Driver implementation: this method shall be implemented if setting
    2912             :  * scale is supported.
    2913             :  *
    2914             :  * @param dfScale scale
    2915             :  * @param eStorageType Data type to which create the potential attribute that
    2916             :  * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
    2917             :  * implementation will decide automatically the data type. Note that changing
    2918             :  * the data type after initial setting might not be supported.
    2919             :  * @return true in case of success.
    2920             :  */
    2921           0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
    2922             :                            CPL_UNUSED GDALDataType eStorageType)
    2923             : {
    2924           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
    2925           0 :     return false;
    2926             : }
    2927             : 
    2928             : /************************************************************************/
    2929             : /*                               SetOffset)                             */
    2930             : /************************************************************************/
    2931             : 
    2932             : /** Set the offset value to apply to raw values.
    2933             :  *
    2934             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2935             :  *
    2936             :  * This is the same as the C function GDALMDArraySetOffset() /
    2937             :  * GDALMDArraySetOffsetEx().
    2938             :  *
    2939             :  * @note Driver implementation: this method shall be implemented if setting
    2940             :  * offset is supported.
    2941             :  *
    2942             :  * @param dfOffset Offset
    2943             :  * @param eStorageType Data type to which create the potential attribute that
    2944             :  * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
    2945             :  * implementation will decide automatically the data type. Note that changing
    2946             :  * the data type after initial setting might not be supported.
    2947             :  * @return true in case of success.
    2948             :  */
    2949           0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
    2950             :                             CPL_UNUSED GDALDataType eStorageType)
    2951             : {
    2952           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
    2953           0 :     return false;
    2954             : }
    2955             : 
    2956             : /************************************************************************/
    2957             : /*                               GetScale()                             */
    2958             : /************************************************************************/
    2959             : 
    2960             : /** Get the scale value to apply to raw values.
    2961             :  *
    2962             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2963             :  *
    2964             :  * This is the same as the C function GDALMDArrayGetScale().
    2965             :  *
    2966             :  * @note Driver implementation: this method shall be implemented if gettings
    2967             :  * scale is supported.
    2968             :  *
    2969             :  * @param pbHasScale Pointer to a output boolean that will be set to true if
    2970             :  * a scale value exists. Might be nullptr.
    2971             :  * @param peStorageType Pointer to a output GDALDataType that will be set to
    2972             :  * the storage type of the scale value, when known/relevant. Otherwise will be
    2973             :  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
    2974             :  *
    2975             :  * @return the scale value. A 1.0 value might also indicate the
    2976             :  * absence of a scale value.
    2977             :  */
    2978          20 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
    2979             :                              CPL_UNUSED GDALDataType *peStorageType) const
    2980             : {
    2981          20 :     if (pbHasScale)
    2982          20 :         *pbHasScale = false;
    2983          20 :     return 1.0;
    2984             : }
    2985             : 
    2986             : /************************************************************************/
    2987             : /*                               GetOffset()                            */
    2988             : /************************************************************************/
    2989             : 
    2990             : /** Get the offset value to apply to raw values.
    2991             :  *
    2992             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2993             :  *
    2994             :  * This is the same as the C function GDALMDArrayGetOffset().
    2995             :  *
    2996             :  * @note Driver implementation: this method shall be implemented if gettings
    2997             :  * offset is supported.
    2998             :  *
    2999             :  * @param pbHasOffset Pointer to a output boolean that will be set to true if
    3000             :  * a offset value exists. Might be nullptr.
    3001             :  * @param peStorageType Pointer to a output GDALDataType that will be set to
    3002             :  * the storage type of the offset value, when known/relevant. Otherwise will be
    3003             :  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
    3004             :  *
    3005             :  * @return the offset value. A 0.0 value might also indicate the
    3006             :  * absence of a offset value.
    3007             :  */
    3008          20 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
    3009             :                               CPL_UNUSED GDALDataType *peStorageType) const
    3010             : {
    3011          20 :     if (pbHasOffset)
    3012          20 :         *pbHasOffset = false;
    3013          20 :     return 0.0;
    3014             : }
    3015             : 
    3016             : /************************************************************************/
    3017             : /*                         ProcessPerChunk()                            */
    3018             : /************************************************************************/
    3019             : 
    3020             : namespace
    3021             : {
    3022             : enum class Caller
    3023             : {
    3024             :     CALLER_END_OF_LOOP,
    3025             :     CALLER_IN_LOOP,
    3026             : };
    3027             : }
    3028             : 
    3029             : /** \brief Call a user-provided function to operate on an array chunk by chunk.
    3030             :  *
    3031             :  * This method is to be used when doing operations on an array, or a subset of
    3032             :  * it, in a chunk by chunk way.
    3033             :  *
    3034             :  * @param arrayStartIdx Values representing the starting index to use
    3035             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    3036             :  *                      Array of GetDimensionCount() values. Must not be
    3037             :  *                      nullptr, unless for a zero-dimensional array.
    3038             :  *
    3039             :  * @param count         Values representing the number of values to use in
    3040             :  *                      each dimension.
    3041             :  *                      Array of GetDimensionCount() values. Must not be
    3042             :  *                      nullptr, unless for a zero-dimensional array.
    3043             :  *
    3044             :  * @param chunkSize     Values representing the chunk size in each dimension.
    3045             :  *                      Might typically the output of GetProcessingChunkSize().
    3046             :  *                      Array of GetDimensionCount() values. Must not be
    3047             :  *                      nullptr, unless for a zero-dimensional array.
    3048             :  *
    3049             :  * @param pfnFunc       User-provided function of type FuncProcessPerChunkType.
    3050             :  *                      Must NOT be nullptr.
    3051             :  *
    3052             :  * @param pUserData     Pointer to pass as the value of the pUserData argument
    3053             :  * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
    3054             :  *
    3055             :  * @return true in case of success.
    3056             :  */
    3057          68 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
    3058             :                                           const GUInt64 *count,
    3059             :                                           const size_t *chunkSize,
    3060             :                                           FuncProcessPerChunkType pfnFunc,
    3061             :                                           void *pUserData)
    3062             : {
    3063          68 :     const auto &dims = GetDimensions();
    3064          68 :     if (dims.empty())
    3065             :     {
    3066           2 :         return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
    3067             :     }
    3068             : 
    3069             :     // Sanity check
    3070          66 :     size_t nTotalChunkSize = 1;
    3071         172 :     for (size_t i = 0; i < dims.size(); i++)
    3072             :     {
    3073         113 :         const auto nSizeThisDim(dims[i]->GetSize());
    3074         113 :         if (count[i] == 0 || count[i] > nSizeThisDim ||
    3075         111 :             arrayStartIdx[i] > nSizeThisDim - count[i])
    3076             :         {
    3077           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    3078             :                      "Inconsistent arrayStartIdx[] / count[] values "
    3079             :                      "regarding array size");
    3080           4 :             return false;
    3081             :         }
    3082         216 :         if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
    3083         107 :             chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
    3084             :         {
    3085           3 :             CPLError(CE_Failure, CPLE_AppDefined,
    3086             :                      "Inconsistent chunkSize[] values");
    3087           3 :             return false;
    3088             :         }
    3089         106 :         nTotalChunkSize *= chunkSize[i];
    3090             :     }
    3091             : 
    3092          59 :     size_t dimIdx = 0;
    3093         118 :     std::vector<GUInt64> chunkArrayStartIdx(dims.size());
    3094         118 :     std::vector<size_t> chunkCount(dims.size());
    3095             : 
    3096             :     struct Stack
    3097             :     {
    3098             :         GUInt64 nBlockCounter = 0;
    3099             :         GUInt64 nBlocksMinusOne = 0;
    3100             :         size_t first_count = 0;  // only used if nBlocks > 1
    3101             :         Caller return_point = Caller::CALLER_END_OF_LOOP;
    3102             :     };
    3103             : 
    3104         118 :     std::vector<Stack> stack(dims.size());
    3105          59 :     GUInt64 iCurChunk = 0;
    3106          59 :     GUInt64 nChunkCount = 1;
    3107         164 :     for (size_t i = 0; i < dims.size(); i++)
    3108             :     {
    3109         105 :         const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
    3110         105 :         const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
    3111         105 :         stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
    3112         105 :         nChunkCount *= 1 + stack[i].nBlocksMinusOne;
    3113         105 :         if (stack[i].nBlocksMinusOne == 0)
    3114             :         {
    3115         100 :             chunkArrayStartIdx[i] = arrayStartIdx[i];
    3116         100 :             chunkCount[i] = static_cast<size_t>(count[i]);
    3117             :         }
    3118             :         else
    3119             :         {
    3120           5 :             stack[i].first_count = static_cast<size_t>(
    3121           5 :                 (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
    3122             :         }
    3123             :     }
    3124             : 
    3125          59 : lbl_next_depth:
    3126         274 :     if (dimIdx == dims.size())
    3127             :     {
    3128          92 :         ++iCurChunk;
    3129          92 :         if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
    3130             :                      iCurChunk, nChunkCount, pUserData))
    3131             :         {
    3132           0 :             return false;
    3133             :         }
    3134             :     }
    3135             :     else
    3136             :     {
    3137         182 :         if (stack[dimIdx].nBlocksMinusOne != 0)
    3138             :         {
    3139          11 :             stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
    3140          11 :             chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
    3141          11 :             chunkCount[dimIdx] = stack[dimIdx].first_count;
    3142          11 :             stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
    3143             :             while (true)
    3144             :             {
    3145          33 :                 dimIdx++;
    3146          33 :                 goto lbl_next_depth;
    3147          33 :             lbl_return_to_caller_in_loop:
    3148          33 :                 --stack[dimIdx].nBlockCounter;
    3149          33 :                 if (stack[dimIdx].nBlockCounter == 0)
    3150          11 :                     break;
    3151          22 :                 chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    3152          22 :                 chunkCount[dimIdx] = chunkSize[dimIdx];
    3153             :             }
    3154             : 
    3155          11 :             chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    3156          22 :             chunkCount[dimIdx] =
    3157          11 :                 static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
    3158          11 :                                     chunkArrayStartIdx[dimIdx]);
    3159          11 :             stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
    3160             :         }
    3161         182 :         dimIdx++;
    3162         182 :         goto lbl_next_depth;
    3163         182 :     lbl_return_to_caller_end_of_loop:
    3164         182 :         if (dimIdx == 0)
    3165          59 :             goto end;
    3166             :     }
    3167             : 
    3168         215 :     assert(dimIdx > 0);
    3169         215 :     dimIdx--;
    3170             :     // cppcheck-suppress negativeContainerIndex
    3171         215 :     switch (stack[dimIdx].return_point)
    3172             :     {
    3173         182 :         case Caller::CALLER_END_OF_LOOP:
    3174         182 :             goto lbl_return_to_caller_end_of_loop;
    3175          33 :         case Caller::CALLER_IN_LOOP:
    3176          33 :             goto lbl_return_to_caller_in_loop;
    3177             :     }
    3178          59 : end:
    3179          59 :     return true;
    3180             : }
    3181             : 
    3182             : /************************************************************************/
    3183             : /*                          GDALAttribute()                             */
    3184             : /************************************************************************/
    3185             : 
    3186             : //! @cond Doxygen_Suppress
    3187       14387 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
    3188           0 :                              CPL_UNUSED const std::string &osName)
    3189             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
    3190       14387 :     : GDALAbstractMDArray(osParentName, osName)
    3191             : #endif
    3192             : {
    3193       14387 : }
    3194             : 
    3195             : //! @endcond
    3196             : 
    3197             : /************************************************************************/
    3198             : /*                        GetDimensionSize()                            */
    3199             : /************************************************************************/
    3200             : 
    3201             : /** Return the size of the dimensions of the attribute.
    3202             :  *
    3203             :  * This will be an empty array for a scalar (single value) attribute.
    3204             :  *
    3205             :  * This is the same as the C function GDALAttributeGetDimensionsSize().
    3206             :  */
    3207         367 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
    3208             : {
    3209         367 :     const auto &dims = GetDimensions();
    3210         367 :     std::vector<GUInt64> ret;
    3211         367 :     ret.reserve(dims.size());
    3212         461 :     for (const auto &dim : dims)
    3213          94 :         ret.push_back(dim->GetSize());
    3214         367 :     return ret;
    3215             : }
    3216             : 
    3217             : /************************************************************************/
    3218             : /*                            GDALRawResult()                           */
    3219             : /************************************************************************/
    3220             : 
    3221             : //! @cond Doxygen_Suppress
    3222         155 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
    3223         155 :                              size_t nEltCount)
    3224         310 :     : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
    3225         155 :       m_raw(raw)
    3226             : {
    3227         155 : }
    3228             : 
    3229             : //! @endcond
    3230             : 
    3231             : /************************************************************************/
    3232             : /*                            GDALRawResult()                           */
    3233             : /************************************************************************/
    3234             : 
    3235             : /** Move constructor. */
    3236           0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
    3237           0 :     : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
    3238           0 :       m_nSize(other.m_nSize), m_raw(other.m_raw)
    3239             : {
    3240           0 :     other.m_nEltCount = 0;
    3241           0 :     other.m_nSize = 0;
    3242           0 :     other.m_raw = nullptr;
    3243           0 : }
    3244             : 
    3245             : /************************************************************************/
    3246             : /*                               FreeMe()                               */
    3247             : /************************************************************************/
    3248             : 
    3249         155 : void GDALRawResult::FreeMe()
    3250             : {
    3251         155 :     if (m_raw && m_dt.NeedsFreeDynamicMemory())
    3252             :     {
    3253          50 :         GByte *pabyPtr = m_raw;
    3254          50 :         const auto nDTSize(m_dt.GetSize());
    3255         100 :         for (size_t i = 0; i < m_nEltCount; ++i)
    3256             :         {
    3257          50 :             m_dt.FreeDynamicMemory(pabyPtr);
    3258          50 :             pabyPtr += nDTSize;
    3259             :         }
    3260             :     }
    3261         155 :     VSIFree(m_raw);
    3262         155 : }
    3263             : 
    3264             : /************************************************************************/
    3265             : /*                             operator=()                              */
    3266             : /************************************************************************/
    3267             : 
    3268             : /** Move assignment. */
    3269           0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
    3270             : {
    3271           0 :     FreeMe();
    3272           0 :     m_dt = std::move(other.m_dt);
    3273           0 :     m_nEltCount = other.m_nEltCount;
    3274           0 :     m_nSize = other.m_nSize;
    3275           0 :     m_raw = other.m_raw;
    3276           0 :     other.m_nEltCount = 0;
    3277           0 :     other.m_nSize = 0;
    3278           0 :     other.m_raw = nullptr;
    3279           0 :     return *this;
    3280             : }
    3281             : 
    3282             : /************************************************************************/
    3283             : /*                         ~GDALRawResult()                             */
    3284             : /************************************************************************/
    3285             : 
    3286             : /** Destructor. */
    3287         155 : GDALRawResult::~GDALRawResult()
    3288             : {
    3289         155 :     FreeMe();
    3290         155 : }
    3291             : 
    3292             : /************************************************************************/
    3293             : /*                            StealData()                               */
    3294             : /************************************************************************/
    3295             : 
    3296             : //! @cond Doxygen_Suppress
    3297             : /** Return buffer to caller which becomes owner of it.
    3298             :  * Only to be used by GDALAttributeReadAsRaw().
    3299             :  */
    3300           6 : GByte *GDALRawResult::StealData()
    3301             : {
    3302           6 :     GByte *ret = m_raw;
    3303           6 :     m_raw = nullptr;
    3304           6 :     m_nEltCount = 0;
    3305           6 :     m_nSize = 0;
    3306           6 :     return ret;
    3307             : }
    3308             : 
    3309             : //! @endcond
    3310             : 
    3311             : /************************************************************************/
    3312             : /*                             ReadAsRaw()                              */
    3313             : /************************************************************************/
    3314             : 
    3315             : /** Return the raw value of an attribute.
    3316             :  *
    3317             :  *
    3318             :  * This is the same as the C function GDALAttributeReadAsRaw().
    3319             :  */
    3320         155 : GDALRawResult GDALAttribute::ReadAsRaw() const
    3321             : {
    3322         155 :     const auto nEltCount(GetTotalElementsCount());
    3323         155 :     const auto &dt(GetDataType());
    3324         155 :     const auto nDTSize(dt.GetSize());
    3325             :     GByte *res = static_cast<GByte *>(
    3326         155 :         VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
    3327         155 :     if (!res)
    3328           0 :         return GDALRawResult(nullptr, dt, 0);
    3329         155 :     const auto &dims = GetDimensions();
    3330         155 :     const auto nDims = GetDimensionCount();
    3331         310 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3332         310 :     std::vector<size_t> count(1 + nDims);
    3333         177 :     for (size_t i = 0; i < nDims; i++)
    3334             :     {
    3335          22 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3336             :     }
    3337         155 :     if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
    3338         155 :               &res[0], static_cast<size_t>(nEltCount * nDTSize)))
    3339             :     {
    3340           0 :         VSIFree(res);
    3341           0 :         return GDALRawResult(nullptr, dt, 0);
    3342             :     }
    3343         155 :     return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
    3344             : }
    3345             : 
    3346             : /************************************************************************/
    3347             : /*                            ReadAsString()                            */
    3348             : /************************************************************************/
    3349             : 
    3350             : /** Return the value of an attribute as a string.
    3351             :  *
    3352             :  * The returned string should not be freed, and its lifetime does not
    3353             :  * excess a next call to ReadAsString() on the same object, or the deletion
    3354             :  * of the object itself.
    3355             :  *
    3356             :  * This function will only return the first element if there are several.
    3357             :  *
    3358             :  * This is the same as the C function GDALAttributeReadAsString()
    3359             :  *
    3360             :  * @return a string, or nullptr.
    3361             :  */
    3362        1368 : const char *GDALAttribute::ReadAsString() const
    3363             : {
    3364        1368 :     const auto nDims = GetDimensionCount();
    3365        2736 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3366        2736 :     std::vector<size_t> count(1 + nDims, 1);
    3367        1368 :     char *szRet = nullptr;
    3368        1368 :     if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
    3369        1368 :               GDALExtendedDataType::CreateString(), &szRet, &szRet,
    3370        4103 :               sizeof(szRet)) ||
    3371        1367 :         szRet == nullptr)
    3372             :     {
    3373           4 :         return nullptr;
    3374             :     }
    3375        1364 :     m_osCachedVal = szRet;
    3376        1364 :     CPLFree(szRet);
    3377        1364 :     return m_osCachedVal.c_str();
    3378             : }
    3379             : 
    3380             : /************************************************************************/
    3381             : /*                            ReadAsInt()                               */
    3382             : /************************************************************************/
    3383             : 
    3384             : /** Return the value of an attribute as a integer.
    3385             :  *
    3386             :  * This function will only return the first element if there are several.
    3387             :  *
    3388             :  * It can fail if its value can not be converted to integer.
    3389             :  *
    3390             :  * This is the same as the C function GDALAttributeReadAsInt()
    3391             :  *
    3392             :  * @return a integer, or INT_MIN in case of error.
    3393             :  */
    3394         226 : int GDALAttribute::ReadAsInt() const
    3395             : {
    3396         226 :     const auto nDims = GetDimensionCount();
    3397         452 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3398         226 :     std::vector<size_t> count(1 + nDims, 1);
    3399         226 :     int nRet = INT_MIN;
    3400         226 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3401         452 :          GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
    3402         452 :     return nRet;
    3403             : }
    3404             : 
    3405             : /************************************************************************/
    3406             : /*                            ReadAsInt64()                             */
    3407             : /************************************************************************/
    3408             : 
    3409             : /** Return the value of an attribute as an int64_t.
    3410             :  *
    3411             :  * This function will only return the first element if there are several.
    3412             :  *
    3413             :  * It can fail if its value can not be converted to long.
    3414             :  *
    3415             :  * This is the same as the C function GDALAttributeReadAsInt64()
    3416             :  *
    3417             :  * @return an int64_t, or INT64_MIN in case of error.
    3418             :  */
    3419          54 : int64_t GDALAttribute::ReadAsInt64() const
    3420             : {
    3421          54 :     const auto nDims = GetDimensionCount();
    3422         108 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3423          54 :     std::vector<size_t> count(1 + nDims, 1);
    3424          54 :     int64_t nRet = INT64_MIN;
    3425          54 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3426         108 :          GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
    3427         108 :     return nRet;
    3428             : }
    3429             : 
    3430             : /************************************************************************/
    3431             : /*                            ReadAsDouble()                            */
    3432             : /************************************************************************/
    3433             : 
    3434             : /** Return the value of an attribute as a double.
    3435             :  *
    3436             :  * This function will only return the first element if there are several.
    3437             :  *
    3438             :  * It can fail if its value can not be converted to double.
    3439             :  *
    3440             :  * This is the same as the C function GDALAttributeReadAsInt()
    3441             :  *
    3442             :  * @return a double value.
    3443             :  */
    3444         349 : double GDALAttribute::ReadAsDouble() const
    3445             : {
    3446         349 :     const auto nDims = GetDimensionCount();
    3447         698 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3448         349 :     std::vector<size_t> count(1 + nDims, 1);
    3449         349 :     double dfRet = 0;
    3450         349 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3451         349 :          GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
    3452         349 :          sizeof(dfRet));
    3453         698 :     return dfRet;
    3454             : }
    3455             : 
    3456             : /************************************************************************/
    3457             : /*                          ReadAsStringArray()                         */
    3458             : /************************************************************************/
    3459             : 
    3460             : /** Return the value of an attribute as an array of strings.
    3461             :  *
    3462             :  * This is the same as the C function GDALAttributeReadAsStringArray()
    3463             :  */
    3464         126 : CPLStringList GDALAttribute::ReadAsStringArray() const
    3465             : {
    3466         126 :     const auto nElts = GetTotalElementsCount();
    3467         126 :     if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
    3468           0 :         return CPLStringList();
    3469             :     char **papszList = static_cast<char **>(
    3470         126 :         VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
    3471         126 :     const auto &dims = GetDimensions();
    3472         126 :     const auto nDims = GetDimensionCount();
    3473         252 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3474         252 :     std::vector<size_t> count(1 + nDims);
    3475         195 :     for (size_t i = 0; i < nDims; i++)
    3476             :     {
    3477          69 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3478             :     }
    3479         126 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3480         126 :          GDALExtendedDataType::CreateString(), papszList, papszList,
    3481         126 :          sizeof(char *) * static_cast<int>(nElts));
    3482         329 :     for (int i = 0; i < static_cast<int>(nElts); i++)
    3483             :     {
    3484         203 :         if (papszList[i] == nullptr)
    3485          13 :             papszList[i] = CPLStrdup("");
    3486             :     }
    3487         126 :     return CPLStringList(papszList);
    3488             : }
    3489             : 
    3490             : /************************************************************************/
    3491             : /*                          ReadAsIntArray()                            */
    3492             : /************************************************************************/
    3493             : 
    3494             : /** Return the value of an attribute as an array of integers.
    3495             :  *
    3496             :  * This is the same as the C function GDALAttributeReadAsIntArray().
    3497             :  */
    3498          15 : std::vector<int> GDALAttribute::ReadAsIntArray() const
    3499             : {
    3500          15 :     const auto nElts = GetTotalElementsCount();
    3501             : #if SIZEOF_VOIDP == 4
    3502             :     if (nElts > static_cast<size_t>(nElts))
    3503             :         return {};
    3504             : #endif
    3505          15 :     std::vector<int> res(static_cast<size_t>(nElts));
    3506          15 :     const auto &dims = GetDimensions();
    3507          15 :     const auto nDims = GetDimensionCount();
    3508          30 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3509          30 :     std::vector<size_t> count(1 + nDims);
    3510          32 :     for (size_t i = 0; i < nDims; i++)
    3511             :     {
    3512          17 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3513             :     }
    3514          15 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3515          30 :          GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
    3516          15 :          res.size() * sizeof(res[0]));
    3517          30 :     return res;
    3518             : }
    3519             : 
    3520             : /************************************************************************/
    3521             : /*                          ReadAsInt64Array()                          */
    3522             : /************************************************************************/
    3523             : 
    3524             : /** Return the value of an attribute as an array of int64_t.
    3525             :  *
    3526             :  * This is the same as the C function GDALAttributeReadAsInt64Array().
    3527             :  */
    3528          38 : std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
    3529             : {
    3530          38 :     const auto nElts = GetTotalElementsCount();
    3531             : #if SIZEOF_VOIDP == 4
    3532             :     if (nElts > static_cast<size_t>(nElts))
    3533             :         return {};
    3534             : #endif
    3535          38 :     std::vector<int64_t> res(static_cast<size_t>(nElts));
    3536          38 :     const auto &dims = GetDimensions();
    3537          38 :     const auto nDims = GetDimensionCount();
    3538          76 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3539          76 :     std::vector<size_t> count(1 + nDims);
    3540          76 :     for (size_t i = 0; i < nDims; i++)
    3541             :     {
    3542          38 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3543             :     }
    3544          38 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3545          76 :          GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
    3546          38 :          res.size() * sizeof(res[0]));
    3547          76 :     return res;
    3548             : }
    3549             : 
    3550             : /************************************************************************/
    3551             : /*                         ReadAsDoubleArray()                          */
    3552             : /************************************************************************/
    3553             : 
    3554             : /** Return the value of an attribute as an array of double.
    3555             :  *
    3556             :  * This is the same as the C function GDALAttributeReadAsDoubleArray().
    3557             :  */
    3558          88 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
    3559             : {
    3560          88 :     const auto nElts = GetTotalElementsCount();
    3561             : #if SIZEOF_VOIDP == 4
    3562             :     if (nElts > static_cast<size_t>(nElts))
    3563             :         return {};
    3564             : #endif
    3565          88 :     std::vector<double> res(static_cast<size_t>(nElts));
    3566          88 :     const auto &dims = GetDimensions();
    3567          88 :     const auto nDims = GetDimensionCount();
    3568         176 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3569         176 :     std::vector<size_t> count(1 + nDims);
    3570         160 :     for (size_t i = 0; i < nDims; i++)
    3571             :     {
    3572          72 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3573             :     }
    3574          88 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3575         176 :          GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
    3576          88 :          res.size() * sizeof(res[0]));
    3577         176 :     return res;
    3578             : }
    3579             : 
    3580             : /************************************************************************/
    3581             : /*                               Write()                                */
    3582             : /************************************************************************/
    3583             : 
    3584             : /** Write an attribute from raw values expressed in GetDataType()
    3585             :  *
    3586             :  * The values should be provided in the type of GetDataType() and there should
    3587             :  * be exactly GetTotalElementsCount() of them.
    3588             :  * If GetDataType() is a string, each value should be a char* pointer.
    3589             :  *
    3590             :  * This is the same as the C function GDALAttributeWriteRaw().
    3591             :  *
    3592             :  * @param pabyValue Buffer of nLen bytes.
    3593             :  * @param nLen Size of pabyValue in bytes. Should be equal to
    3594             :  *             GetTotalElementsCount() * GetDataType().GetSize()
    3595             :  * @return true in case of success.
    3596             :  */
    3597          96 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
    3598             : {
    3599          96 :     if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
    3600             :     {
    3601           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3602             :                  "Length is not of expected value");
    3603           0 :         return false;
    3604             :     }
    3605          96 :     const auto &dims = GetDimensions();
    3606          96 :     const auto nDims = GetDimensionCount();
    3607         192 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3608         192 :     std::vector<size_t> count(1 + nDims);
    3609         119 :     for (size_t i = 0; i < nDims; i++)
    3610             :     {
    3611          23 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3612             :     }
    3613          96 :     return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
    3614          96 :                  pabyValue, pabyValue, nLen);
    3615             : }
    3616             : 
    3617             : /************************************************************************/
    3618             : /*                               Write()                                */
    3619             : /************************************************************************/
    3620             : 
    3621             : /** Write an attribute from a string value.
    3622             :  *
    3623             :  * Type conversion will be performed if needed. If the attribute contains
    3624             :  * multiple values, only the first one will be updated.
    3625             :  *
    3626             :  * This is the same as the C function GDALAttributeWriteString().
    3627             :  *
    3628             :  * @param pszValue Pointer to a string.
    3629             :  * @return true in case of success.
    3630             :  */
    3631         304 : bool GDALAttribute::Write(const char *pszValue)
    3632             : {
    3633         304 :     const auto nDims = GetDimensionCount();
    3634         608 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3635         304 :     std::vector<size_t> count(1 + nDims, 1);
    3636         304 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3637         608 :                  GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
    3638         608 :                  sizeof(pszValue));
    3639             : }
    3640             : 
    3641             : /************************************************************************/
    3642             : /*                              WriteInt()                              */
    3643             : /************************************************************************/
    3644             : 
    3645             : /** Write an attribute from a integer value.
    3646             :  *
    3647             :  * Type conversion will be performed if needed. If the attribute contains
    3648             :  * multiple values, only the first one will be updated.
    3649             :  *
    3650             :  * This is the same as the C function GDALAttributeWriteInt().
    3651             :  *
    3652             :  * @param nVal Value.
    3653             :  * @return true in case of success.
    3654             :  */
    3655          22 : bool GDALAttribute::WriteInt(int nVal)
    3656             : {
    3657          22 :     const auto nDims = GetDimensionCount();
    3658          44 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3659          22 :     std::vector<size_t> count(1 + nDims, 1);
    3660          22 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3661          44 :                  GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
    3662          44 :                  sizeof(nVal));
    3663             : }
    3664             : 
    3665             : /************************************************************************/
    3666             : /*                              WriteInt64()                             */
    3667             : /************************************************************************/
    3668             : 
    3669             : /** Write an attribute from an int64_t value.
    3670             :  *
    3671             :  * Type conversion will be performed if needed. If the attribute contains
    3672             :  * multiple values, only the first one will be updated.
    3673             :  *
    3674             :  * This is the same as the C function GDALAttributeWriteInt().
    3675             :  *
    3676             :  * @param nVal Value.
    3677             :  * @return true in case of success.
    3678             :  */
    3679          11 : bool GDALAttribute::WriteInt64(int64_t nVal)
    3680             : {
    3681          11 :     const auto nDims = GetDimensionCount();
    3682          22 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3683          11 :     std::vector<size_t> count(1 + nDims, 1);
    3684          11 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3685          22 :                  GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
    3686          22 :                  sizeof(nVal));
    3687             : }
    3688             : 
    3689             : /************************************************************************/
    3690             : /*                                Write()                               */
    3691             : /************************************************************************/
    3692             : 
    3693             : /** Write an attribute from a double value.
    3694             :  *
    3695             :  * Type conversion will be performed if needed. If the attribute contains
    3696             :  * multiple values, only the first one will be updated.
    3697             :  *
    3698             :  * This is the same as the C function GDALAttributeWriteDouble().
    3699             :  *
    3700             :  * @param dfVal Value.
    3701             :  * @return true in case of success.
    3702             :  */
    3703          38 : bool GDALAttribute::Write(double dfVal)
    3704             : {
    3705          38 :     const auto nDims = GetDimensionCount();
    3706          76 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3707          38 :     std::vector<size_t> count(1 + nDims, 1);
    3708          38 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3709          76 :                  GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
    3710          76 :                  sizeof(dfVal));
    3711             : }
    3712             : 
    3713             : /************************************************************************/
    3714             : /*                                Write()                               */
    3715             : /************************************************************************/
    3716             : 
    3717             : /** Write an attribute from an array of strings.
    3718             :  *
    3719             :  * Type conversion will be performed if needed.
    3720             :  *
    3721             :  * Exactly GetTotalElementsCount() strings must be provided
    3722             :  *
    3723             :  * This is the same as the C function GDALAttributeWriteStringArray().
    3724             :  *
    3725             :  * @param vals Array of strings.
    3726             :  * @return true in case of success.
    3727             :  */
    3728           8 : bool GDALAttribute::Write(CSLConstList vals)
    3729             : {
    3730           8 :     if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
    3731             :     {
    3732           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3733           1 :         return false;
    3734             :     }
    3735           7 :     const auto nDims = GetDimensionCount();
    3736          14 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3737           7 :     std::vector<size_t> count(1 + nDims);
    3738           7 :     const auto &dims = GetDimensions();
    3739          15 :     for (size_t i = 0; i < nDims; i++)
    3740           8 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3741           7 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3742           7 :                  GDALExtendedDataType::CreateString(), vals, vals,
    3743          14 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
    3744             : }
    3745             : 
    3746             : /************************************************************************/
    3747             : /*                                Write()                               */
    3748             : /************************************************************************/
    3749             : 
    3750             : /** Write an attribute from an array of int.
    3751             :  *
    3752             :  * Type conversion will be performed if needed.
    3753             :  *
    3754             :  * Exactly GetTotalElementsCount() strings must be provided
    3755             :  *
    3756             :  * This is the same as the C function GDALAttributeWriteIntArray()
    3757             :  *
    3758             :  * @param vals Array of int.
    3759             :  * @param nVals Should be equal to GetTotalElementsCount().
    3760             :  * @return true in case of success.
    3761             :  */
    3762           9 : bool GDALAttribute::Write(const int *vals, size_t nVals)
    3763             : {
    3764           9 :     if (nVals != GetTotalElementsCount())
    3765             :     {
    3766           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3767           1 :         return false;
    3768             :     }
    3769           8 :     const auto nDims = GetDimensionCount();
    3770          16 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3771           8 :     std::vector<size_t> count(1 + nDims);
    3772           8 :     const auto &dims = GetDimensions();
    3773          16 :     for (size_t i = 0; i < nDims; i++)
    3774           8 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3775           8 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3776           8 :                  GDALExtendedDataType::Create(GDT_Int32), vals, vals,
    3777          16 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
    3778             : }
    3779             : 
    3780             : /************************************************************************/
    3781             : /*                                Write()                               */
    3782             : /************************************************************************/
    3783             : 
    3784             : /** Write an attribute from an array of int64_t.
    3785             :  *
    3786             :  * Type conversion will be performed if needed.
    3787             :  *
    3788             :  * Exactly GetTotalElementsCount() strings must be provided
    3789             :  *
    3790             :  * This is the same as the C function GDALAttributeWriteLongArray()
    3791             :  *
    3792             :  * @param vals Array of int64_t.
    3793             :  * @param nVals Should be equal to GetTotalElementsCount().
    3794             :  * @return true in case of success.
    3795             :  */
    3796          10 : bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
    3797             : {
    3798          10 :     if (nVals != GetTotalElementsCount())
    3799             :     {
    3800           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3801           0 :         return false;
    3802             :     }
    3803          10 :     const auto nDims = GetDimensionCount();
    3804          20 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3805          10 :     std::vector<size_t> count(1 + nDims);
    3806          10 :     const auto &dims = GetDimensions();
    3807          20 :     for (size_t i = 0; i < nDims; i++)
    3808          10 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3809          10 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3810          10 :                  GDALExtendedDataType::Create(GDT_Int64), vals, vals,
    3811          10 :                  static_cast<size_t>(GetTotalElementsCount()) *
    3812          10 :                      sizeof(int64_t));
    3813             : }
    3814             : 
    3815             : /************************************************************************/
    3816             : /*                                Write()                               */
    3817             : /************************************************************************/
    3818             : 
    3819             : /** Write an attribute from an array of double.
    3820             :  *
    3821             :  * Type conversion will be performed if needed.
    3822             :  *
    3823             :  * Exactly GetTotalElementsCount() strings must be provided
    3824             :  *
    3825             :  * This is the same as the C function GDALAttributeWriteDoubleArray()
    3826             :  *
    3827             :  * @param vals Array of double.
    3828             :  * @param nVals Should be equal to GetTotalElementsCount().
    3829             :  * @return true in case of success.
    3830             :  */
    3831           7 : bool GDALAttribute::Write(const double *vals, size_t nVals)
    3832             : {
    3833           7 :     if (nVals != GetTotalElementsCount())
    3834             :     {
    3835           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3836           1 :         return false;
    3837             :     }
    3838           6 :     const auto nDims = GetDimensionCount();
    3839          12 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3840           6 :     std::vector<size_t> count(1 + nDims);
    3841           6 :     const auto &dims = GetDimensions();
    3842          13 :     for (size_t i = 0; i < nDims; i++)
    3843           7 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3844           6 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3845           6 :                  GDALExtendedDataType::Create(GDT_Float64), vals, vals,
    3846          12 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
    3847             : }
    3848             : 
    3849             : /************************************************************************/
    3850             : /*                           GDALMDArray()                              */
    3851             : /************************************************************************/
    3852             : 
    3853             : //! @cond Doxygen_Suppress
    3854        6568 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
    3855             :                          CPL_UNUSED const std::string &osName,
    3856           0 :                          const std::string &osContext)
    3857             :     :
    3858             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
    3859             :       GDALAbstractMDArray(osParentName, osName),
    3860             : #endif
    3861        6568 :       m_osContext(osContext)
    3862             : {
    3863        6568 : }
    3864             : 
    3865             : //! @endcond
    3866             : 
    3867             : /************************************************************************/
    3868             : /*                           GetTotalCopyCost()                         */
    3869             : /************************************************************************/
    3870             : 
    3871             : /** Return a total "cost" to copy the array.
    3872             :  *
    3873             :  * Used as a parameter for CopyFrom()
    3874             :  */
    3875          50 : GUInt64 GDALMDArray::GetTotalCopyCost() const
    3876             : {
    3877         100 :     return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
    3878         100 :            GetTotalElementsCount() * GetDataType().GetSize();
    3879             : }
    3880             : 
    3881             : /************************************************************************/
    3882             : /*                       CopyFromAllExceptValues()                      */
    3883             : /************************************************************************/
    3884             : 
    3885             : //! @cond Doxygen_Suppress
    3886             : 
    3887         175 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
    3888             :                                           bool bStrict, GUInt64 &nCurCost,
    3889             :                                           const GUInt64 nTotalCost,
    3890             :                                           GDALProgressFunc pfnProgress,
    3891             :                                           void *pProgressData)
    3892             : {
    3893             :     // Nodata setting must be one of the first things done for TileDB
    3894         175 :     const void *pNoData = poSrcArray->GetRawNoDataValue();
    3895         175 :     if (pNoData && poSrcArray->GetDataType() == GetDataType())
    3896             :     {
    3897          13 :         SetRawNoDataValue(pNoData);
    3898             :     }
    3899             : 
    3900         175 :     const bool bThisIsUnscaledArray =
    3901         175 :         dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
    3902         350 :     auto attrs = poSrcArray->GetAttributes();
    3903         222 :     for (const auto &attr : attrs)
    3904             :     {
    3905          47 :         const auto &osAttrName = attr->GetName();
    3906          47 :         if (bThisIsUnscaledArray)
    3907             :         {
    3908           6 :             if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
    3909           7 :                 osAttrName == "valid_min" || osAttrName == "valid_max" ||
    3910           1 :                 osAttrName == "valid_range")
    3911             :             {
    3912           1 :                 continue;
    3913             :             }
    3914             :         }
    3915             : 
    3916          46 :         auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
    3917          92 :                                        attr->GetDataType());
    3918          46 :         if (!dstAttr)
    3919             :         {
    3920           0 :             if (bStrict)
    3921           0 :                 return false;
    3922           0 :             continue;
    3923             :         }
    3924          46 :         auto raw = attr->ReadAsRaw();
    3925          46 :         if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
    3926           0 :             return false;
    3927             :     }
    3928         175 :     if (!attrs.empty())
    3929             :     {
    3930          26 :         nCurCost += attrs.size() * GDALAttribute::COPY_COST;
    3931          46 :         if (pfnProgress &&
    3932          20 :             !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    3933           0 :             return false;
    3934             :     }
    3935             : 
    3936         175 :     auto srcSRS = poSrcArray->GetSpatialRef();
    3937         175 :     if (srcSRS)
    3938             :     {
    3939          11 :         SetSpatialRef(srcSRS.get());
    3940             :     }
    3941             : 
    3942         175 :     const std::string &osUnit(poSrcArray->GetUnit());
    3943         175 :     if (!osUnit.empty())
    3944             :     {
    3945          18 :         SetUnit(osUnit);
    3946             :     }
    3947             : 
    3948         175 :     bool bGotValue = false;
    3949         175 :     GDALDataType eOffsetStorageType = GDT_Unknown;
    3950             :     const double dfOffset =
    3951         175 :         poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
    3952         175 :     if (bGotValue)
    3953             :     {
    3954           3 :         SetOffset(dfOffset, eOffsetStorageType);
    3955             :     }
    3956             : 
    3957         175 :     bGotValue = false;
    3958         175 :     GDALDataType eScaleStorageType = GDT_Unknown;
    3959         175 :     const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
    3960         175 :     if (bGotValue)
    3961             :     {
    3962           3 :         SetScale(dfScale, eScaleStorageType);
    3963             :     }
    3964             : 
    3965         175 :     return true;
    3966             : }
    3967             : 
    3968             : //! @endcond
    3969             : 
    3970             : /************************************************************************/
    3971             : /*                               CopyFrom()                             */
    3972             : /************************************************************************/
    3973             : 
    3974             : /** Copy the content of an array into a new (generally empty) array.
    3975             :  *
    3976             :  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
    3977             :  *                   of some output drivers this is not recommended)
    3978             :  * @param poSrcArray Source array. Should NOT be nullptr.
    3979             :  * @param bStrict Whether to enable strict mode. In strict mode, any error will
    3980             :  *                stop the copy. In relaxed mode, the copy will be attempted to
    3981             :  *                be pursued.
    3982             :  * @param nCurCost  Should be provided as a variable initially set to 0.
    3983             :  * @param nTotalCost Total cost from GetTotalCopyCost().
    3984             :  * @param pfnProgress Progress callback, or nullptr.
    3985             :  * @param pProgressData Progress user data, or nulptr.
    3986             :  *
    3987             :  * @return true in case of success (or partial success if bStrict == false).
    3988             :  */
    3989          48 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
    3990             :                            const GDALMDArray *poSrcArray, bool bStrict,
    3991             :                            GUInt64 &nCurCost, const GUInt64 nTotalCost,
    3992             :                            GDALProgressFunc pfnProgress, void *pProgressData)
    3993             : {
    3994          48 :     if (pfnProgress == nullptr)
    3995           4 :         pfnProgress = GDALDummyProgress;
    3996             : 
    3997          48 :     nCurCost += GDALMDArray::COPY_COST;
    3998             : 
    3999          48 :     if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
    4000             :                                  pfnProgress, pProgressData))
    4001             :     {
    4002           0 :         return false;
    4003             :     }
    4004             : 
    4005          48 :     const auto &dims = poSrcArray->GetDimensions();
    4006          48 :     const auto nDTSize = poSrcArray->GetDataType().GetSize();
    4007          48 :     if (dims.empty())
    4008             :     {
    4009           2 :         std::vector<GByte> abyTmp(nDTSize);
    4010           2 :         if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
    4011           2 :                                GetDataType(), &abyTmp[0]) &&
    4012           2 :               Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
    4013           4 :                     &abyTmp[0])) &&
    4014             :             bStrict)
    4015             :         {
    4016           0 :             return false;
    4017             :         }
    4018           2 :         nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
    4019           2 :         if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    4020           0 :             return false;
    4021             :     }
    4022             :     else
    4023             :     {
    4024          46 :         std::vector<GUInt64> arrayStartIdx(dims.size());
    4025          46 :         std::vector<GUInt64> count(dims.size());
    4026         125 :         for (size_t i = 0; i < dims.size(); i++)
    4027             :         {
    4028          79 :             count[i] = static_cast<size_t>(dims[i]->GetSize());
    4029             :         }
    4030             : 
    4031             :         struct CopyFunc
    4032             :         {
    4033             :             GDALMDArray *poDstArray = nullptr;
    4034             :             std::vector<GByte> abyTmp{};
    4035             :             GDALProgressFunc pfnProgress = nullptr;
    4036             :             void *pProgressData = nullptr;
    4037             :             GUInt64 nCurCost = 0;
    4038             :             GUInt64 nTotalCost = 0;
    4039             :             GUInt64 nTotalBytesThisArray = 0;
    4040             :             bool bStop = false;
    4041             : 
    4042          64 :             static bool f(GDALAbstractMDArray *l_poSrcArray,
    4043             :                           const GUInt64 *chunkArrayStartIdx,
    4044             :                           const size_t *chunkCount, GUInt64 iCurChunk,
    4045             :                           GUInt64 nChunkCount, void *pUserData)
    4046             :             {
    4047          64 :                 const auto &dt(l_poSrcArray->GetDataType());
    4048          64 :                 auto data = static_cast<CopyFunc *>(pUserData);
    4049          64 :                 auto poDstArray = data->poDstArray;
    4050          64 :                 if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
    4051          64 :                                         nullptr, dt, &data->abyTmp[0]))
    4052             :                 {
    4053           0 :                     return false;
    4054             :                 }
    4055             :                 bool bRet =
    4056          64 :                     poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
    4057          64 :                                       nullptr, dt, &data->abyTmp[0]);
    4058          64 :                 if (dt.NeedsFreeDynamicMemory())
    4059             :                 {
    4060           4 :                     const auto l_nDTSize = dt.GetSize();
    4061           4 :                     GByte *ptr = &data->abyTmp[0];
    4062           4 :                     const size_t l_nDims(l_poSrcArray->GetDimensionCount());
    4063           4 :                     size_t nEltCount = 1;
    4064           8 :                     for (size_t i = 0; i < l_nDims; ++i)
    4065             :                     {
    4066           4 :                         nEltCount *= chunkCount[i];
    4067             :                     }
    4068          20 :                     for (size_t i = 0; i < nEltCount; i++)
    4069             :                     {
    4070          16 :                         dt.FreeDynamicMemory(ptr);
    4071          16 :                         ptr += l_nDTSize;
    4072             :                     }
    4073             :                 }
    4074          64 :                 if (!bRet)
    4075             :                 {
    4076           0 :                     return false;
    4077             :                 }
    4078             : 
    4079          64 :                 double dfCurCost =
    4080          64 :                     double(data->nCurCost) + double(iCurChunk) / nChunkCount *
    4081          64 :                                                  data->nTotalBytesThisArray;
    4082          64 :                 if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
    4083             :                                        data->pProgressData))
    4084             :                 {
    4085           0 :                     data->bStop = true;
    4086           0 :                     return false;
    4087             :                 }
    4088             : 
    4089          64 :                 return true;
    4090             :             }
    4091             :         };
    4092             : 
    4093          46 :         CopyFunc copyFunc;
    4094          46 :         copyFunc.poDstArray = this;
    4095          46 :         copyFunc.nCurCost = nCurCost;
    4096          46 :         copyFunc.nTotalCost = nTotalCost;
    4097          46 :         copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
    4098          46 :         copyFunc.pfnProgress = pfnProgress;
    4099          46 :         copyFunc.pProgressData = pProgressData;
    4100             :         const char *pszSwathSize =
    4101          46 :             CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
    4102             :         const size_t nMaxChunkSize =
    4103             :             pszSwathSize
    4104          46 :                 ? static_cast<size_t>(
    4105           1 :                       std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    4106           1 :                                CPLAtoGIntBig(pszSwathSize)))
    4107             :                 : static_cast<size_t>(
    4108          45 :                       std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    4109          45 :                                GDALGetCacheMax64() / 4));
    4110          46 :         const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
    4111          46 :         size_t nRealChunkSize = nDTSize;
    4112         125 :         for (const auto &nChunkSize : anChunkSizes)
    4113             :         {
    4114          79 :             nRealChunkSize *= nChunkSize;
    4115             :         }
    4116             :         try
    4117             :         {
    4118          46 :             copyFunc.abyTmp.resize(nRealChunkSize);
    4119             :         }
    4120           0 :         catch (const std::exception &)
    4121             :         {
    4122           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    4123             :                      "Cannot allocate temporary buffer");
    4124           0 :             nCurCost += copyFunc.nTotalBytesThisArray;
    4125           0 :             return false;
    4126             :         }
    4127         137 :         if (copyFunc.nTotalBytesThisArray != 0 &&
    4128          45 :             !const_cast<GDALMDArray *>(poSrcArray)
    4129          45 :                  ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
    4130             :                                    anChunkSizes.data(), CopyFunc::f,
    4131          91 :                                    &copyFunc) &&
    4132           0 :             (bStrict || copyFunc.bStop))
    4133             :         {
    4134           0 :             nCurCost += copyFunc.nTotalBytesThisArray;
    4135           0 :             return false;
    4136             :         }
    4137          46 :         nCurCost += copyFunc.nTotalBytesThisArray;
    4138             :     }
    4139             : 
    4140          48 :     return true;
    4141             : }
    4142             : 
    4143             : /************************************************************************/
    4144             : /*                         GetStructuralInfo()                          */
    4145             : /************************************************************************/
    4146             : 
    4147             : /** Return structural information on the array.
    4148             :  *
    4149             :  * This may be the compression, etc..
    4150             :  *
    4151             :  * The return value should not be freed and is valid until GDALMDArray is
    4152             :  * released or this function called again.
    4153             :  *
    4154             :  * This is the same as the C function GDALMDArrayGetStructuralInfo().
    4155             :  */
    4156          95 : CSLConstList GDALMDArray::GetStructuralInfo() const
    4157             : {
    4158          95 :     return nullptr;
    4159             : }
    4160             : 
    4161             : /************************************************************************/
    4162             : /*                          AdviseRead()                                */
    4163             : /************************************************************************/
    4164             : 
    4165             : /** Advise driver of upcoming read requests.
    4166             :  *
    4167             :  * Some GDAL drivers operate more efficiently if they know in advance what
    4168             :  * set of upcoming read requests will be made.  The AdviseRead() method allows
    4169             :  * an application to notify the driver of the region of interest.
    4170             :  *
    4171             :  * Many drivers just ignore the AdviseRead() call, but it can dramatically
    4172             :  * accelerate access via some drivers. One such case is when reading through
    4173             :  * a DAP dataset with the netCDF driver (a in-memory cache array is then created
    4174             :  * with the region of interest defined by AdviseRead())
    4175             :  *
    4176             :  * This is the same as the C function GDALMDArrayAdviseRead().
    4177             :  *
    4178             :  * @param arrayStartIdx Values representing the starting index to read
    4179             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    4180             :  *                      Array of GetDimensionCount() values.
    4181             :  *                      Can be nullptr as a synonymous for [0 for i in
    4182             :  * range(GetDimensionCount() ]
    4183             :  *
    4184             :  * @param count         Values representing the number of values to extract in
    4185             :  *                      each dimension.
    4186             :  *                      Array of GetDimensionCount() values.
    4187             :  *                      Can be nullptr as a synonymous for
    4188             :  *                      [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
    4189             :  * range(GetDimensionCount() ]
    4190             :  *
    4191             :  * @param papszOptions Driver specific options, or nullptr. Consult driver
    4192             :  * documentation.
    4193             :  *
    4194             :  * @return true in case of success (ignoring the advice is a success)
    4195             :  *
    4196             :  * @since GDAL 3.2
    4197             :  */
    4198          25 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    4199             :                              CSLConstList papszOptions) const
    4200             : {
    4201          25 :     const auto nDimCount = GetDimensionCount();
    4202          25 :     if (nDimCount == 0)
    4203           2 :         return true;
    4204             : 
    4205          46 :     std::vector<GUInt64> tmp_arrayStartIdx;
    4206          23 :     if (arrayStartIdx == nullptr)
    4207             :     {
    4208           0 :         tmp_arrayStartIdx.resize(nDimCount);
    4209           0 :         arrayStartIdx = tmp_arrayStartIdx.data();
    4210             :     }
    4211             : 
    4212          46 :     std::vector<size_t> tmp_count;
    4213          23 :     if (count == nullptr)
    4214             :     {
    4215           0 :         tmp_count.resize(nDimCount);
    4216           0 :         const auto &dims = GetDimensions();
    4217           0 :         for (size_t i = 0; i < nDimCount; i++)
    4218             :         {
    4219           0 :             const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
    4220             : #if SIZEOF_VOIDP < 8
    4221             :             if (nSize != static_cast<size_t>(nSize))
    4222             :             {
    4223             :                 CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
    4224             :                 return false;
    4225             :             }
    4226             : #endif
    4227           0 :             tmp_count[i] = static_cast<size_t>(nSize);
    4228             :         }
    4229           0 :         count = tmp_count.data();
    4230             :     }
    4231             : 
    4232          46 :     std::vector<GInt64> tmp_arrayStep;
    4233          46 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    4234          23 :     const GInt64 *arrayStep = nullptr;
    4235          23 :     const GPtrDiff_t *bufferStride = nullptr;
    4236          23 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    4237          46 :                               GDALExtendedDataType::Create(GDT_Unknown),
    4238             :                               nullptr, nullptr, 0, tmp_arrayStep,
    4239             :                               tmp_bufferStride))
    4240             :     {
    4241           1 :         return false;
    4242             :     }
    4243             : 
    4244          22 :     return IAdviseRead(arrayStartIdx, count, papszOptions);
    4245             : }
    4246             : 
    4247             : /************************************************************************/
    4248             : /*                             IAdviseRead()                            */
    4249             : /************************************************************************/
    4250             : 
    4251             : //! @cond Doxygen_Suppress
    4252           3 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
    4253             :                               CSLConstList /* papszOptions*/) const
    4254             : {
    4255           3 :     return true;
    4256             : }
    4257             : 
    4258             : //! @endcond
    4259             : 
    4260             : /************************************************************************/
    4261             : /*                            MassageName()                             */
    4262             : /************************************************************************/
    4263             : 
    4264             : //! @cond Doxygen_Suppress
    4265          32 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
    4266             : {
    4267          32 :     std::string ret;
    4268         604 :     for (const char ch : inputName)
    4269             :     {
    4270         572 :         if (!isalnum(static_cast<unsigned char>(ch)))
    4271         138 :             ret += '_';
    4272             :         else
    4273         434 :             ret += ch;
    4274             :     }
    4275          32 :     return ret;
    4276             : }
    4277             : 
    4278             : //! @endcond
    4279             : 
    4280             : /************************************************************************/
    4281             : /*                         GetCacheRootGroup()                          */
    4282             : /************************************************************************/
    4283             : 
    4284             : //! @cond Doxygen_Suppress
    4285             : std::shared_ptr<GDALGroup>
    4286        1442 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
    4287             :                                std::string &osCacheFilenameOut) const
    4288             : {
    4289        1442 :     const auto &osFilename = GetFilename();
    4290        1442 :     if (osFilename.empty())
    4291             :     {
    4292           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4293             :                  "Cannot cache an array with an empty filename");
    4294           1 :         return nullptr;
    4295             :     }
    4296             : 
    4297        1441 :     osCacheFilenameOut = osFilename + ".gmac";
    4298        1441 :     if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
    4299             :     {
    4300           0 :         const auto nPosQuestionMark = osFilename.find('?');
    4301           0 :         if (nPosQuestionMark != std::string::npos)
    4302             :         {
    4303             :             osCacheFilenameOut =
    4304           0 :                 osFilename.substr(0, nPosQuestionMark)
    4305           0 :                     .append(".gmac")
    4306           0 :                     .append(osFilename.substr(nPosQuestionMark));
    4307             :         }
    4308             :     }
    4309        1441 :     const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
    4310        1441 :     if (pszProxy != nullptr)
    4311           7 :         osCacheFilenameOut = pszProxy;
    4312             : 
    4313        1441 :     std::unique_ptr<GDALDataset> poDS;
    4314             :     VSIStatBufL sStat;
    4315        1441 :     if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
    4316             :     {
    4317          28 :         poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
    4318             :                                      GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
    4319             :                                      nullptr, nullptr, nullptr));
    4320             :     }
    4321        1441 :     if (poDS)
    4322             :     {
    4323          28 :         CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
    4324          28 :         return poDS->GetRootGroup();
    4325             :     }
    4326             : 
    4327        1413 :     if (bCanCreate)
    4328             :     {
    4329           4 :         const char *pszDrvName = "netCDF";
    4330           4 :         GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
    4331           4 :         if (poDrv == nullptr)
    4332             :         {
    4333           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
    4334             :                      pszDrvName);
    4335           0 :             return nullptr;
    4336             :         }
    4337             :         {
    4338           8 :             CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
    4339           8 :             CPLErrorStateBackuper oErrorStateBackuper;
    4340           4 :             poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
    4341             :                                                      nullptr, nullptr));
    4342             :         }
    4343           4 :         if (!poDS)
    4344             :         {
    4345           1 :             pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
    4346           1 :             if (pszProxy)
    4347             :             {
    4348           1 :                 osCacheFilenameOut = pszProxy;
    4349           1 :                 poDS.reset(poDrv->CreateMultiDimensional(
    4350             :                     osCacheFilenameOut.c_str(), nullptr, nullptr));
    4351             :             }
    4352             :         }
    4353           4 :         if (poDS)
    4354             :         {
    4355           4 :             CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
    4356           4 :             return poDS->GetRootGroup();
    4357             :         }
    4358             :         else
    4359             :         {
    4360           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4361             :                      "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
    4362             :                      "configuration option to write the cache in "
    4363             :                      "another directory",
    4364             :                      osCacheFilenameOut.c_str());
    4365             :         }
    4366             :     }
    4367             : 
    4368        1409 :     return nullptr;
    4369             : }
    4370             : 
    4371             : //! @endcond
    4372             : 
    4373             : /************************************************************************/
    4374             : /*                              Cache()                                 */
    4375             : /************************************************************************/
    4376             : 
    4377             : /** Cache the content of the array into an auxiliary filename.
    4378             :  *
    4379             :  * The main purpose of this method is to be able to cache views that are
    4380             :  * expensive to compute, such as transposed arrays.
    4381             :  *
    4382             :  * The array will be stored in a file whose name is the one of
    4383             :  * GetFilename(), with an extra .gmac extension (stands for GDAL
    4384             :  * Multidimensional Array Cache). The cache is a netCDF dataset.
    4385             :  *
    4386             :  * If the .gmac file cannot be written next to the dataset, the
    4387             :  * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
    4388             :  * directory.
    4389             :  *
    4390             :  * The GDALMDArray::Read() method will automatically use the cache when it
    4391             :  * exists. There is no timestamp checks between the source array and the cached
    4392             :  * array. If the source arrays changes, the cache must be manually deleted.
    4393             :  *
    4394             :  * This is the same as the C function GDALMDArrayCache()
    4395             :  *
    4396             :  * @note Driver implementation: optionally implemented.
    4397             :  *
    4398             :  * @param papszOptions List of options, null terminated, or NULL. Currently
    4399             :  *                     the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
    4400             :  *                     to specify the block size of the cached array.
    4401             :  * @return true in case of success.
    4402             :  */
    4403           7 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
    4404             : {
    4405          14 :     std::string osCacheFilename;
    4406          14 :     auto poRG = GetCacheRootGroup(true, osCacheFilename);
    4407           7 :     if (!poRG)
    4408           1 :         return false;
    4409             : 
    4410          12 :     const std::string osCachedArrayName(MassageName(GetFullName()));
    4411           6 :     if (poRG->OpenMDArray(osCachedArrayName))
    4412             :     {
    4413           2 :         CPLError(CE_Failure, CPLE_NotSupported,
    4414             :                  "An array with same name %s already exists in %s",
    4415             :                  osCachedArrayName.c_str(), osCacheFilename.c_str());
    4416           2 :         return false;
    4417             :     }
    4418             : 
    4419           8 :     CPLStringList aosOptions;
    4420           4 :     aosOptions.SetNameValue("COMPRESS", "DEFLATE");
    4421           4 :     const auto &aoDims = GetDimensions();
    4422           8 :     std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
    4423           4 :     if (!aoDims.empty())
    4424             :     {
    4425             :         std::string osBlockSize(
    4426           4 :             CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
    4427           4 :         if (osBlockSize.empty())
    4428             :         {
    4429           6 :             const auto anBlockSize = GetBlockSize();
    4430           3 :             int idxDim = 0;
    4431          10 :             for (auto nBlockSize : anBlockSize)
    4432             :             {
    4433           7 :                 if (idxDim > 0)
    4434           4 :                     osBlockSize += ',';
    4435           7 :                 if (nBlockSize == 0)
    4436           7 :                     nBlockSize = 256;
    4437           7 :                 nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
    4438             :                 osBlockSize +=
    4439           7 :                     std::to_string(static_cast<uint64_t>(nBlockSize));
    4440           7 :                 idxDim++;
    4441             :             }
    4442             :         }
    4443           4 :         aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
    4444             : 
    4445           4 :         int idxDim = 0;
    4446          13 :         for (const auto &poDim : aoDims)
    4447             :         {
    4448           9 :             auto poNewDim = poRG->CreateDimension(
    4449          18 :                 osCachedArrayName + '_' + std::to_string(idxDim),
    4450          18 :                 poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
    4451           9 :             if (!poNewDim)
    4452           0 :                 return false;
    4453           9 :             aoNewDims.emplace_back(poNewDim);
    4454           9 :             idxDim++;
    4455             :         }
    4456             :     }
    4457             : 
    4458           4 :     auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
    4459           8 :                                              GetDataType(), aosOptions.List());
    4460           4 :     if (!poCachedArray)
    4461             :     {
    4462           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
    4463             :                  osCachedArrayName.c_str(), osCacheFilename.c_str());
    4464           0 :         return false;
    4465             :     }
    4466             : 
    4467           4 :     GUInt64 nCost = 0;
    4468           8 :     return poCachedArray->CopyFrom(nullptr, this,
    4469             :                                    false,  // strict
    4470           4 :                                    nCost, GetTotalCopyCost(), nullptr, nullptr);
    4471             : }
    4472             : 
    4473             : /************************************************************************/
    4474             : /*                               Read()                                 */
    4475             : /************************************************************************/
    4476             : 
    4477        3899 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
    4478             :                        const GInt64 *arrayStep,         // step in elements
    4479             :                        const GPtrDiff_t *bufferStride,  // stride in elements
    4480             :                        const GDALExtendedDataType &bufferDataType,
    4481             :                        void *pDstBuffer, const void *pDstBufferAllocStart,
    4482             :                        size_t nDstBufferAllocSize) const
    4483             : {
    4484        3899 :     if (!m_bHasTriedCachedArray)
    4485             :     {
    4486        1748 :         m_bHasTriedCachedArray = true;
    4487        1748 :         if (IsCacheable())
    4488             :         {
    4489        1748 :             const auto &osFilename = GetFilename();
    4490        2986 :             if (!osFilename.empty() &&
    4491        2986 :                 !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
    4492             :             {
    4493        2456 :                 std::string osCacheFilename;
    4494        2456 :                 auto poRG = GetCacheRootGroup(false, osCacheFilename);
    4495        1228 :                 if (poRG)
    4496             :                 {
    4497             :                     const std::string osCachedArrayName(
    4498          32 :                         MassageName(GetFullName()));
    4499          16 :                     m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
    4500          16 :                     if (m_poCachedArray)
    4501             :                     {
    4502           6 :                         const auto &dims = GetDimensions();
    4503             :                         const auto &cachedDims =
    4504           6 :                             m_poCachedArray->GetDimensions();
    4505           6 :                         const size_t nDims = dims.size();
    4506             :                         bool ok =
    4507          12 :                             m_poCachedArray->GetDataType() == GetDataType() &&
    4508           6 :                             cachedDims.size() == nDims;
    4509          19 :                         for (size_t i = 0; ok && i < nDims; ++i)
    4510             :                         {
    4511          13 :                             ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
    4512             :                         }
    4513           6 :                         if (ok)
    4514             :                         {
    4515           6 :                             CPLDebug("GDAL", "Cached array for %s found in %s",
    4516             :                                      osCachedArrayName.c_str(),
    4517             :                                      osCacheFilename.c_str());
    4518             :                         }
    4519             :                         else
    4520             :                         {
    4521           0 :                             CPLError(CE_Warning, CPLE_AppDefined,
    4522             :                                      "Cached array %s in %s has incompatible "
    4523             :                                      "characteristics with current array.",
    4524             :                                      osCachedArrayName.c_str(),
    4525             :                                      osCacheFilename.c_str());
    4526           0 :                             m_poCachedArray.reset();
    4527             :                         }
    4528             :                     }
    4529             :                 }
    4530             :             }
    4531             :         }
    4532             :     }
    4533             : 
    4534        3899 :     const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
    4535        3899 :     if (!array->GetDataType().CanConvertTo(bufferDataType))
    4536             :     {
    4537           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4538             :                  "Array data type is not convertible to buffer data type");
    4539           0 :         return false;
    4540             :     }
    4541             : 
    4542        7798 :     std::vector<GInt64> tmp_arrayStep;
    4543        7798 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    4544        3899 :     if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
    4545             :                                      bufferStride, bufferDataType, pDstBuffer,
    4546             :                                      pDstBufferAllocStart, nDstBufferAllocSize,
    4547             :                                      tmp_arrayStep, tmp_bufferStride))
    4548             :     {
    4549           9 :         return false;
    4550             :     }
    4551             : 
    4552        3890 :     return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
    4553        3890 :                         bufferDataType, pDstBuffer);
    4554             : }
    4555             : 
    4556             : /************************************************************************/
    4557             : /*                          GetRootGroup()                              */
    4558             : /************************************************************************/
    4559             : 
    4560             : /** Return the root group to which this arrays belongs too.
    4561             :  *
    4562             :  * Note that arrays may be free standing and some drivers may not implement
    4563             :  * this method, hence nullptr may be returned.
    4564             :  *
    4565             :  * It is used internally by the GetResampled() method to detect if GLT
    4566             :  * orthorectification is available.
    4567             :  *
    4568             :  * @return the root group, or nullptr.
    4569             :  * @since GDAL 3.8
    4570             :  */
    4571           0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
    4572             : {
    4573           0 :     return nullptr;
    4574             : }
    4575             : 
    4576             : //! @cond Doxygen_Suppress
    4577             : 
    4578             : /************************************************************************/
    4579             : /*                       IsTransposedRequest()                          */
    4580             : /************************************************************************/
    4581             : 
    4582         784 : bool GDALMDArray::IsTransposedRequest(
    4583             :     const size_t *count,
    4584             :     const GPtrDiff_t *bufferStride) const  // stride in elements
    4585             : {
    4586             :     /*
    4587             :     For example:
    4588             :     count = [2,3,4]
    4589             :     strides = [12, 4, 1]            (2-1)*12+(3-1)*4+(4-1)*1=23   ==> row major
    4590             :     stride [12, 1, 3]            (2-1)*12+(3-1)*1+(4-1)*3=23   ==>
    4591             :     (axis[0],axis[2],axis[1]) transposition [1, 8, 2]             (2-1)*1+
    4592             :     (3-1)*8+(4-1)*2=23   ==> (axis[2],axis[1],axis[0]) transposition
    4593             :     */
    4594         784 :     const size_t nDims(GetDimensionCount());
    4595         784 :     size_t nCurStrideForRowMajorStrides = 1;
    4596         784 :     bool bRowMajorStrides = true;
    4597         784 :     size_t nElts = 1;
    4598         784 :     size_t nLastIdx = 0;
    4599        2157 :     for (size_t i = nDims; i > 0;)
    4600             :     {
    4601        1373 :         --i;
    4602        1373 :         if (bufferStride[i] < 0)
    4603           0 :             return false;
    4604        1373 :         if (static_cast<size_t>(bufferStride[i]) !=
    4605             :             nCurStrideForRowMajorStrides)
    4606             :         {
    4607         258 :             bRowMajorStrides = false;
    4608             :         }
    4609             :         // Integer overflows have already been checked in CheckReadWriteParams()
    4610        1373 :         nCurStrideForRowMajorStrides *= count[i];
    4611        1373 :         nElts *= count[i];
    4612        1373 :         nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
    4613             :     }
    4614         784 :     if (bRowMajorStrides)
    4615         598 :         return false;
    4616         186 :     return nLastIdx == nElts - 1;
    4617             : }
    4618             : 
    4619             : /************************************************************************/
    4620             : /*                   CopyToFinalBufferSameDataType()                    */
    4621             : /************************************************************************/
    4622             : 
    4623             : template <size_t N>
    4624          60 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
    4625             :                                    size_t nDims, const size_t *count,
    4626             :                                    const GPtrDiff_t *bufferStride)
    4627             : {
    4628         120 :     std::vector<size_t> anStackCount(nDims);
    4629         120 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    4630          60 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
    4631             : #if defined(__GNUC__)
    4632             : #pragma GCC diagnostic push
    4633             : #pragma GCC diagnostic ignored "-Wnull-dereference"
    4634             : #endif
    4635          60 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    4636             : #if defined(__GNUC__)
    4637             : #pragma GCC diagnostic pop
    4638             : #endif
    4639          60 :     size_t iDim = 0;
    4640             : 
    4641         749 : lbl_next_depth:
    4642         749 :     if (iDim == nDims - 1)
    4643             :     {
    4644         661 :         size_t n = count[iDim];
    4645         661 :         GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
    4646         661 :         const auto bufferStrideLastDim = bufferStride[iDim] * N;
    4647       29186 :         while (n > 0)
    4648             :         {
    4649       28525 :             --n;
    4650       28525 :             memcpy(pabyDstBuffer, pabySrcBuffer, N);
    4651       28525 :             pabyDstBuffer += bufferStrideLastDim;
    4652       28525 :             pabySrcBuffer += N;
    4653             :         }
    4654             :     }
    4655             :     else
    4656             :     {
    4657          88 :         anStackCount[iDim] = count[iDim];
    4658             :         while (true)
    4659             :         {
    4660         689 :             ++iDim;
    4661         689 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    4662         689 :             goto lbl_next_depth;
    4663         689 :         lbl_return_to_caller_in_loop:
    4664         689 :             --iDim;
    4665         689 :             --anStackCount[iDim];
    4666         689 :             if (anStackCount[iDim] == 0)
    4667          88 :                 break;
    4668         601 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
    4669             :         }
    4670             :     }
    4671         749 :     if (iDim > 0)
    4672         689 :         goto lbl_return_to_caller_in_loop;
    4673          60 : }
    4674             : 
    4675             : /************************************************************************/
    4676             : /*                        CopyToFinalBuffer()                           */
    4677             : /************************************************************************/
    4678             : 
    4679         166 : static void CopyToFinalBuffer(const void *pSrcBuffer,
    4680             :                               const GDALExtendedDataType &eSrcDataType,
    4681             :                               void *pDstBuffer,
    4682             :                               const GDALExtendedDataType &eDstDataType,
    4683             :                               size_t nDims, const size_t *count,
    4684             :                               const GPtrDiff_t *bufferStride)
    4685             : {
    4686         166 :     const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
    4687             :     // Use specialized implementation for well-known data types when no
    4688             :     // type conversion is needed
    4689         166 :     if (eSrcDataType == eDstDataType)
    4690             :     {
    4691         110 :         if (nSrcDataTypeSize == 1)
    4692             :         {
    4693          41 :             CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
    4694             :                                              count, bufferStride);
    4695          60 :             return;
    4696             :         }
    4697          69 :         else if (nSrcDataTypeSize == 2)
    4698             :         {
    4699           1 :             CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
    4700             :                                              count, bufferStride);
    4701           1 :             return;
    4702             :         }
    4703          68 :         else if (nSrcDataTypeSize == 4)
    4704             :         {
    4705          14 :             CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
    4706             :                                              count, bufferStride);
    4707          14 :             return;
    4708             :         }
    4709          54 :         else if (nSrcDataTypeSize == 8)
    4710             :         {
    4711           4 :             CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
    4712             :                                              count, bufferStride);
    4713           4 :             return;
    4714             :         }
    4715             :     }
    4716             : 
    4717         106 :     const size_t nDstDataTypeSize(eDstDataType.GetSize());
    4718         212 :     std::vector<size_t> anStackCount(nDims);
    4719         212 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    4720         106 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
    4721         106 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    4722         106 :     size_t iDim = 0;
    4723             : 
    4724         375 : lbl_next_depth:
    4725         375 :     if (iDim == nDims - 1)
    4726             :     {
    4727         365 :         GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
    4728         365 :                                          pabyDstBufferStack[iDim], eDstDataType,
    4729         365 :                                          bufferStride[iDim], count[iDim]);
    4730         365 :         pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
    4731             :     }
    4732             :     else
    4733             :     {
    4734          10 :         anStackCount[iDim] = count[iDim];
    4735             :         while (true)
    4736             :         {
    4737         269 :             ++iDim;
    4738         269 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    4739         269 :             goto lbl_next_depth;
    4740         269 :         lbl_return_to_caller_in_loop:
    4741         269 :             --iDim;
    4742         269 :             --anStackCount[iDim];
    4743         269 :             if (anStackCount[iDim] == 0)
    4744          10 :                 break;
    4745         259 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
    4746             :         }
    4747             :     }
    4748         375 :     if (iDim > 0)
    4749         269 :         goto lbl_return_to_caller_in_loop;
    4750             : }
    4751             : 
    4752             : /************************************************************************/
    4753             : /*                      TransposeLast2Dims()                            */
    4754             : /************************************************************************/
    4755             : 
    4756          19 : static bool TransposeLast2Dims(void *pDstBuffer,
    4757             :                                const GDALExtendedDataType &eDT,
    4758             :                                const size_t nDims, const size_t *count,
    4759             :                                const size_t nEltsNonLast2Dims)
    4760             : {
    4761          19 :     const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
    4762          19 :     const auto nDTSize = eDT.GetSize();
    4763             :     void *pTempBufferForLast2DimsTranspose =
    4764          19 :         VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
    4765          19 :     if (pTempBufferForLast2DimsTranspose == nullptr)
    4766           0 :         return false;
    4767             : 
    4768          19 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
    4769          58 :     for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
    4770             :     {
    4771          39 :         GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
    4772             :                         pTempBufferForLast2DimsTranspose,
    4773          39 :                         eDT.GetNumericDataType(), count[nDims - 1],
    4774          39 :                         count[nDims - 2]);
    4775          39 :         memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
    4776             :                nDTSize * nEltsLast2Dims);
    4777          39 :         pabyDstBuffer += nDTSize * nEltsLast2Dims;
    4778             :     }
    4779             : 
    4780          19 :     VSIFree(pTempBufferForLast2DimsTranspose);
    4781             : 
    4782          19 :     return true;
    4783             : }
    4784             : 
    4785             : /************************************************************************/
    4786             : /*                      ReadForTransposedRequest()                      */
    4787             : /************************************************************************/
    4788             : 
    4789             : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
    4790             : // transposed view yield to extremely poor/unusable performance. This fixes
    4791             : // this by using temporary memory to read in a contiguous buffer in a
    4792             : // row-major order, and then do the transposition to the final buffer.
    4793             : 
    4794         185 : bool GDALMDArray::ReadForTransposedRequest(
    4795             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    4796             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
    4797             :     void *pDstBuffer) const
    4798             : {
    4799         185 :     const size_t nDims(GetDimensionCount());
    4800         185 :     if (nDims == 0)
    4801             :     {
    4802           0 :         CPLAssert(false);
    4803             :         return false;  // shouldn't happen
    4804             :     }
    4805         185 :     size_t nElts = 1;
    4806         492 :     for (size_t i = 0; i < nDims; ++i)
    4807         307 :         nElts *= count[i];
    4808             : 
    4809         370 :     std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
    4810         185 :     tmpBufferStrides.back() = 1;
    4811         307 :     for (size_t i = nDims - 1; i > 0;)
    4812             :     {
    4813         122 :         --i;
    4814         122 :         tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
    4815             :     }
    4816             : 
    4817         185 :     const auto &eDT = GetDataType();
    4818         185 :     const auto nDTSize = eDT.GetSize();
    4819         314 :     if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
    4820         330 :         static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
    4821          16 :         (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
    4822             :     {
    4823             :         // Optimization of the optimization if only the last 2 dims are
    4824             :         // transposed that saves on temporary buffer allocation
    4825          23 :         const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
    4826          23 :         size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
    4827          23 :         bool bRowMajorStridesForNonLast2Dims = true;
    4828          23 :         size_t nEltsNonLast2Dims = 1;
    4829          40 :         for (size_t i = nDims - 2; i > 0;)
    4830             :         {
    4831          17 :             --i;
    4832          17 :             if (static_cast<size_t>(bufferStride[i]) !=
    4833             :                 nCurStrideForRowMajorStrides)
    4834             :             {
    4835           4 :                 bRowMajorStridesForNonLast2Dims = false;
    4836             :             }
    4837             :             // Integer overflows have already been checked in
    4838             :             // CheckReadWriteParams()
    4839          17 :             nCurStrideForRowMajorStrides *= count[i];
    4840          17 :             nEltsNonLast2Dims *= count[i];
    4841             :         }
    4842          23 :         if (bRowMajorStridesForNonLast2Dims)
    4843             :         {
    4844             :             // We read in the final buffer!
    4845          19 :             if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
    4846          19 :                        eDT, pDstBuffer))
    4847             :             {
    4848           0 :                 return false;
    4849             :             }
    4850             : 
    4851          19 :             return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
    4852          19 :                                       nEltsNonLast2Dims);
    4853             :         }
    4854             :     }
    4855             : 
    4856         166 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
    4857         166 :     if (pTempBuffer == nullptr)
    4858           0 :         return false;
    4859             : 
    4860         166 :     if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
    4861         166 :                pTempBuffer))
    4862             :     {
    4863           0 :         VSIFree(pTempBuffer);
    4864           0 :         return false;
    4865             :     }
    4866         166 :     CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
    4867             :                       count, bufferStride);
    4868             : 
    4869         166 :     if (eDT.NeedsFreeDynamicMemory())
    4870             :     {
    4871          95 :         GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
    4872         190 :         for (size_t i = 0; i < nElts; ++i)
    4873             :         {
    4874          95 :             eDT.FreeDynamicMemory(pabyPtr);
    4875          95 :             pabyPtr += nDTSize;
    4876             :         }
    4877             :     }
    4878             : 
    4879         166 :     VSIFree(pTempBuffer);
    4880         166 :     return true;
    4881             : }
    4882             : 
    4883             : /************************************************************************/
    4884             : /*               IsStepOneContiguousRowMajorOrderedSameDataType()       */
    4885             : /************************************************************************/
    4886             : 
    4887             : // Returns true if at all following conditions are met:
    4888             : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
    4889             : // defines a row-major ordered contiguous buffer.
    4890          78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
    4891             :     const size_t *count, const GInt64 *arrayStep,
    4892             :     const GPtrDiff_t *bufferStride,
    4893             :     const GDALExtendedDataType &bufferDataType) const
    4894             : {
    4895          78 :     if (bufferDataType != GetDataType())
    4896           5 :         return false;
    4897          73 :     size_t nExpectedStride = 1;
    4898         166 :     for (size_t i = GetDimensionCount(); i > 0;)
    4899             :     {
    4900          96 :         --i;
    4901          96 :         if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
    4902          94 :             static_cast<size_t>(bufferStride[i]) != nExpectedStride)
    4903             :         {
    4904           3 :             return false;
    4905             :         }
    4906          93 :         nExpectedStride *= count[i];
    4907             :     }
    4908          70 :     return true;
    4909             : }
    4910             : 
    4911             : /************************************************************************/
    4912             : /*                      ReadUsingContiguousIRead()                      */
    4913             : /************************************************************************/
    4914             : 
    4915             : // Used for example by the TileDB driver when requesting it with
    4916             : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
    4917             : // not defining a row-major ordered contiguous buffer.
    4918             : // Should only be called when at least one of the above conditions are true,
    4919             : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
    4920             : // returning none.
    4921             : // This method will call IRead() again with arrayStep[] == 1,
    4922             : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
    4923             : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
    4924             : // content of that temporary buffer onto pDstBuffer.
    4925           7 : bool GDALMDArray::ReadUsingContiguousIRead(
    4926             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    4927             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
    4928             :     void *pDstBuffer) const
    4929             : {
    4930           7 :     const size_t nDims(GetDimensionCount());
    4931          14 :     std::vector<GUInt64> anTmpStartIdx(nDims);
    4932          14 :     std::vector<size_t> anTmpCount(nDims);
    4933           7 :     const auto &oType = GetDataType();
    4934           7 :     size_t nMemArraySize = oType.GetSize();
    4935          14 :     std::vector<GPtrDiff_t> anTmpStride(nDims);
    4936           7 :     GPtrDiff_t nStride = 1;
    4937          18 :     for (size_t i = nDims; i > 0;)
    4938             :     {
    4939          11 :         --i;
    4940          11 :         if (arrayStep[i] > 0)
    4941           9 :             anTmpStartIdx[i] = arrayStartIdx[i];
    4942             :         else
    4943           2 :             anTmpStartIdx[i] =
    4944           2 :                 arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
    4945             :         const uint64_t nCount =
    4946          11 :             (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
    4947          11 :         if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
    4948             :         {
    4949           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4950             :                      "Read() failed due to too large memory requirement");
    4951           0 :             return false;
    4952             :         }
    4953          11 :         anTmpCount[i] = static_cast<size_t>(nCount);
    4954          11 :         nMemArraySize *= anTmpCount[i];
    4955          11 :         anTmpStride[i] = nStride;
    4956          11 :         nStride *= anTmpCount[i];
    4957             :     }
    4958             :     std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
    4959          14 :         VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
    4960           7 :     if (!pTmpBuffer)
    4961           0 :         return false;
    4962           7 :     if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
    4963          14 :                std::vector<GInt64>(nDims, 1).data(),  // steps
    4964           7 :                anTmpStride.data(), oType, pTmpBuffer.get()))
    4965             :     {
    4966           0 :         return false;
    4967             :     }
    4968          14 :     std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
    4969          18 :     for (size_t i = 0; i < nDims; ++i)
    4970             :     {
    4971          11 :         if (arrayStep[i] > 0)
    4972           9 :             anTmpStartIdx[i] = 0;
    4973             :         else
    4974           2 :             anTmpStartIdx[i] = anTmpCount[i] - 1;
    4975          22 :         apoTmpDims[i] = std::make_shared<GDALDimension>(
    4976          22 :             std::string(), std::string(), std::string(), std::string(),
    4977          22 :             anTmpCount[i]);
    4978             :     }
    4979             :     auto poMEMArray =
    4980          14 :         MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
    4981          21 :     return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
    4982           7 :            poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
    4983           7 :                             bufferStride, bufferDataType, pDstBuffer);
    4984             : }
    4985             : 
    4986             : //! @endcond
    4987             : 
    4988             : /************************************************************************/
    4989             : /*                       GDALSlicedMDArray                              */
    4990             : /************************************************************************/
    4991             : 
    4992             : class GDALSlicedMDArray final : public GDALPamMDArray
    4993             : {
    4994             :   private:
    4995             :     std::shared_ptr<GDALMDArray> m_poParent{};
    4996             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    4997             :     std::vector<size_t> m_mapDimIdxToParentDimIdx{};  // of size m_dims.size()
    4998             :     std::vector<Range>
    4999             :         m_parentRanges{};  // of size m_poParent->GetDimensionCount()
    5000             : 
    5001             :     mutable std::vector<GUInt64> m_parentStart;
    5002             :     mutable std::vector<size_t> m_parentCount;
    5003             :     mutable std::vector<GInt64> m_parentStep;
    5004             :     mutable std::vector<GPtrDiff_t> m_parentStride;
    5005             : 
    5006             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
    5007             :                              const GInt64 *arrayStep,
    5008             :                              const GPtrDiff_t *bufferStride) const;
    5009             : 
    5010             :   protected:
    5011         582 :     explicit GDALSlicedMDArray(
    5012             :         const std::shared_ptr<GDALMDArray> &poParent,
    5013             :         const std::string &viewExpr,
    5014             :         std::vector<std::shared_ptr<GDALDimension>> &&dims,
    5015             :         std::vector<size_t> &&mapDimIdxToParentDimIdx,
    5016             :         std::vector<Range> &&parentRanges)
    5017        1746 :         : GDALAbstractMDArray(std::string(), "Sliced view of " +
    5018        1746 :                                                  poParent->GetFullName() +
    5019        1164 :                                                  " (" + viewExpr + ")"),
    5020        1164 :           GDALPamMDArray(std::string(),
    5021        1164 :                          "Sliced view of " + poParent->GetFullName() + " (" +
    5022        1164 :                              viewExpr + ")",
    5023        1164 :                          GDALPamMultiDim::GetPAM(poParent),
    5024             :                          poParent->GetContext()),
    5025        1164 :           m_poParent(std::move(poParent)), m_dims(std::move(dims)),
    5026         582 :           m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
    5027         582 :           m_parentRanges(std::move(parentRanges)),
    5028         582 :           m_parentStart(m_poParent->GetDimensionCount()),
    5029         582 :           m_parentCount(m_poParent->GetDimensionCount(), 1),
    5030         582 :           m_parentStep(m_poParent->GetDimensionCount()),
    5031        4656 :           m_parentStride(m_poParent->GetDimensionCount())
    5032             :     {
    5033         582 :     }
    5034             : 
    5035             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5036             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5037             :                const GDALExtendedDataType &bufferDataType,
    5038             :                void *pDstBuffer) const override;
    5039             : 
    5040             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    5041             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5042             :                 const GDALExtendedDataType &bufferDataType,
    5043             :                 const void *pSrcBuffer) override;
    5044             : 
    5045             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5046             :                      CSLConstList papszOptions) const override;
    5047             : 
    5048             :   public:
    5049             :     static std::shared_ptr<GDALSlicedMDArray>
    5050         582 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5051             :            const std::string &viewExpr,
    5052             :            std::vector<std::shared_ptr<GDALDimension>> &&dims,
    5053             :            std::vector<size_t> &&mapDimIdxToParentDimIdx,
    5054             :            std::vector<Range> &&parentRanges)
    5055             :     {
    5056         582 :         CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
    5057         582 :         CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
    5058             : 
    5059             :         auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
    5060         582 :             poParent, viewExpr, std::move(dims),
    5061         582 :             std::move(mapDimIdxToParentDimIdx), std::move(parentRanges))));
    5062         582 :         newAr->SetSelf(newAr);
    5063         582 :         return newAr;
    5064             :     }
    5065             : 
    5066          56 :     bool IsWritable() const override
    5067             :     {
    5068          56 :         return m_poParent->IsWritable();
    5069             :     }
    5070             : 
    5071         990 :     const std::string &GetFilename() const override
    5072             :     {
    5073         990 :         return m_poParent->GetFilename();
    5074             :     }
    5075             : 
    5076             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5077        3675 :     GetDimensions() const override
    5078             :     {
    5079        3675 :         return m_dims;
    5080             :     }
    5081             : 
    5082        1396 :     const GDALExtendedDataType &GetDataType() const override
    5083             :     {
    5084        1396 :         return m_poParent->GetDataType();
    5085             :     }
    5086             : 
    5087           2 :     const std::string &GetUnit() const override
    5088             :     {
    5089           2 :         return m_poParent->GetUnit();
    5090             :     }
    5091             : 
    5092             :     // bool SetUnit(const std::string& osUnit) override  { return
    5093             :     // m_poParent->SetUnit(osUnit); }
    5094             : 
    5095           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5096             :     {
    5097           4 :         auto poSrcSRS = m_poParent->GetSpatialRef();
    5098           2 :         if (!poSrcSRS)
    5099           1 :             return nullptr;
    5100           2 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
    5101           2 :         std::vector<int> dstMapping;
    5102           3 :         for (int srcAxis : srcMapping)
    5103             :         {
    5104           2 :             bool bFound = false;
    5105           3 :             for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
    5106             :             {
    5107           3 :                 if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
    5108           3 :                     srcAxis - 1)
    5109             :                 {
    5110           2 :                     dstMapping.push_back(static_cast<int>(i) + 1);
    5111           2 :                     bFound = true;
    5112           2 :                     break;
    5113             :                 }
    5114             :             }
    5115           2 :             if (!bFound)
    5116             :             {
    5117           0 :                 dstMapping.push_back(0);
    5118             :             }
    5119             :         }
    5120           2 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
    5121           1 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
    5122           1 :         return poClone;
    5123             :     }
    5124             : 
    5125          57 :     const void *GetRawNoDataValue() const override
    5126             :     {
    5127          57 :         return m_poParent->GetRawNoDataValue();
    5128             :     }
    5129             : 
    5130             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
    5131             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
    5132             : 
    5133           2 :     double GetOffset(bool *pbHasOffset,
    5134             :                      GDALDataType *peStorageType) const override
    5135             :     {
    5136           2 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5137             :     }
    5138             : 
    5139           2 :     double GetScale(bool *pbHasScale,
    5140             :                     GDALDataType *peStorageType) const override
    5141             :     {
    5142           2 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5143             :     }
    5144             : 
    5145             :     // bool SetOffset(double dfOffset) override { return
    5146             :     // m_poParent->SetOffset(dfOffset); }
    5147             : 
    5148             :     // bool SetScale(double dfScale) override { return
    5149             :     // m_poParent->SetScale(dfScale); }
    5150             : 
    5151         198 :     std::vector<GUInt64> GetBlockSize() const override
    5152             :     {
    5153         198 :         std::vector<GUInt64> ret(GetDimensionCount());
    5154         396 :         const auto parentBlockSize(m_poParent->GetBlockSize());
    5155         598 :         for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
    5156             :         {
    5157         400 :             const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
    5158         400 :             if (iOldAxis != static_cast<size_t>(-1))
    5159             :             {
    5160         400 :                 ret[i] = parentBlockSize[iOldAxis];
    5161             :             }
    5162             :         }
    5163         396 :         return ret;
    5164             :     }
    5165             : 
    5166             :     std::shared_ptr<GDALAttribute>
    5167           6 :     GetAttribute(const std::string &osName) const override
    5168             :     {
    5169           6 :         return m_poParent->GetAttribute(osName);
    5170             :     }
    5171             : 
    5172             :     std::vector<std::shared_ptr<GDALAttribute>>
    5173          24 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    5174             :     {
    5175          24 :         return m_poParent->GetAttributes(papszOptions);
    5176             :     }
    5177             : };
    5178             : 
    5179             : /************************************************************************/
    5180             : /*                        PrepareParentArrays()                         */
    5181             : /************************************************************************/
    5182             : 
    5183         476 : void GDALSlicedMDArray::PrepareParentArrays(
    5184             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    5185             :     const GPtrDiff_t *bufferStride) const
    5186             : {
    5187         476 :     const size_t nParentDimCount = m_parentRanges.size();
    5188        1484 :     for (size_t i = 0; i < nParentDimCount; i++)
    5189             :     {
    5190             :         // For dimensions in parent that have no existence in sliced array
    5191        1008 :         m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
    5192             :     }
    5193             : 
    5194        1253 :     for (size_t i = 0; i < m_dims.size(); i++)
    5195             :     {
    5196         777 :         const auto iParent = m_mapDimIdxToParentDimIdx[i];
    5197         777 :         if (iParent != static_cast<size_t>(-1))
    5198             :         {
    5199         775 :             m_parentStart[iParent] =
    5200         775 :                 m_parentRanges[iParent].m_nIncr >= 0
    5201         775 :                     ? m_parentRanges[iParent].m_nStartIdx +
    5202         745 :                           arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
    5203          30 :                     : m_parentRanges[iParent].m_nStartIdx -
    5204          60 :                           arrayStartIdx[i] *
    5205          30 :                               static_cast<GUInt64>(
    5206          30 :                                   -m_parentRanges[iParent].m_nIncr);
    5207         775 :             m_parentCount[iParent] = count[i];
    5208         775 :             if (arrayStep)
    5209             :             {
    5210         774 :                 m_parentStep[iParent] =
    5211         774 :                     count[i] == 1 ? 1 :
    5212             :                                   // other checks should have ensured this does
    5213             :                         // not overflow
    5214         588 :                         arrayStep[i] * m_parentRanges[iParent].m_nIncr;
    5215             :             }
    5216         775 :             if (bufferStride)
    5217             :             {
    5218         774 :                 m_parentStride[iParent] = bufferStride[i];
    5219             :             }
    5220             :         }
    5221             :     }
    5222         476 : }
    5223             : 
    5224             : /************************************************************************/
    5225             : /*                             IRead()                                  */
    5226             : /************************************************************************/
    5227             : 
    5228         443 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5229             :                               const GInt64 *arrayStep,
    5230             :                               const GPtrDiff_t *bufferStride,
    5231             :                               const GDALExtendedDataType &bufferDataType,
    5232             :                               void *pDstBuffer) const
    5233             : {
    5234         443 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5235         886 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
    5236         443 :                             m_parentStep.data(), m_parentStride.data(),
    5237         443 :                             bufferDataType, pDstBuffer);
    5238             : }
    5239             : 
    5240             : /************************************************************************/
    5241             : /*                             IWrite()                                  */
    5242             : /************************************************************************/
    5243             : 
    5244          32 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
    5245             :                                const size_t *count, const GInt64 *arrayStep,
    5246             :                                const GPtrDiff_t *bufferStride,
    5247             :                                const GDALExtendedDataType &bufferDataType,
    5248             :                                const void *pSrcBuffer)
    5249             : {
    5250          32 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5251          64 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
    5252          32 :                              m_parentStep.data(), m_parentStride.data(),
    5253          32 :                              bufferDataType, pSrcBuffer);
    5254             : }
    5255             : 
    5256             : /************************************************************************/
    5257             : /*                             IAdviseRead()                            */
    5258             : /************************************************************************/
    5259             : 
    5260           1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
    5261             :                                     const size_t *count,
    5262             :                                     CSLConstList papszOptions) const
    5263             : {
    5264           1 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
    5265           1 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
    5266           1 :                                   papszOptions);
    5267             : }
    5268             : 
    5269             : /************************************************************************/
    5270             : /*                        CreateSlicedArray()                           */
    5271             : /************************************************************************/
    5272             : 
    5273             : static std::shared_ptr<GDALMDArray>
    5274         600 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
    5275             :                   const std::string &viewExpr, const std::string &activeSlice,
    5276             :                   bool bRenameDimensions,
    5277             :                   std::vector<GDALMDArray::ViewSpec> &viewSpecs)
    5278             : {
    5279         600 :     const auto &srcDims(self->GetDimensions());
    5280         600 :     if (srcDims.empty())
    5281             :     {
    5282           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
    5283           2 :         return nullptr;
    5284             :     }
    5285             : 
    5286        1196 :     CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
    5287         598 :     const auto nTokens = static_cast<size_t>(aosTokens.size());
    5288             : 
    5289        1196 :     std::vector<std::shared_ptr<GDALDimension>> newDims;
    5290        1196 :     std::vector<size_t> mapDimIdxToParentDimIdx;
    5291        1196 :     std::vector<GDALSlicedMDArray::Range> parentRanges;
    5292         598 :     newDims.reserve(nTokens);
    5293         598 :     mapDimIdxToParentDimIdx.reserve(nTokens);
    5294         598 :     parentRanges.reserve(nTokens);
    5295             : 
    5296         598 :     bool bGotEllipsis = false;
    5297         598 :     size_t nCurSrcDim = 0;
    5298        1768 :     for (size_t i = 0; i < nTokens; i++)
    5299             :     {
    5300        1186 :         const char *pszIdxSpec = aosTokens[i];
    5301        1186 :         if (EQUAL(pszIdxSpec, "..."))
    5302             :         {
    5303          38 :             if (bGotEllipsis)
    5304             :             {
    5305           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5306             :                          "Only one single ellipsis is supported");
    5307           2 :                 return nullptr;
    5308             :             }
    5309          36 :             bGotEllipsis = true;
    5310          36 :             const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
    5311          79 :             for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
    5312             :             {
    5313          43 :                 parentRanges.emplace_back(0, 1);
    5314          43 :                 newDims.push_back(srcDims[nCurSrcDim]);
    5315          43 :                 mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5316             :             }
    5317          36 :             continue;
    5318             :         }
    5319        1148 :         else if (EQUAL(pszIdxSpec, "newaxis") ||
    5320        1145 :                  EQUAL(pszIdxSpec, "np.newaxis"))
    5321             :         {
    5322           3 :             newDims.push_back(std::make_shared<GDALDimension>(
    5323           6 :                 std::string(), "newaxis", std::string(), std::string(), 1));
    5324           3 :             mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
    5325           3 :             continue;
    5326             :         }
    5327        1145 :         else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
    5328             :         {
    5329         325 :             if (nCurSrcDim >= srcDims.size())
    5330             :             {
    5331           2 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
    5332             :                          activeSlice.c_str());
    5333           7 :                 return nullptr;
    5334             :             }
    5335             : 
    5336         323 :             auto nVal = CPLAtoGIntBig(pszIdxSpec);
    5337         323 :             GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
    5338         323 :             if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
    5339         319 :                 (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
    5340             :             {
    5341           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5342             :                          "Index " CPL_FRMT_GIB " is out of bounds", nVal);
    5343           5 :                 return nullptr;
    5344             :             }
    5345         318 :             if (nVal < 0)
    5346           0 :                 nVal += nDimSize;
    5347         318 :             parentRanges.emplace_back(nVal, 0);
    5348             :         }
    5349             :         else
    5350             :         {
    5351         820 :             if (nCurSrcDim >= srcDims.size())
    5352             :             {
    5353           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
    5354             :                          activeSlice.c_str());
    5355           7 :                 return nullptr;
    5356             :             }
    5357             : 
    5358             :             CPLStringList aosRangeTokens(
    5359         819 :                 CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
    5360         819 :             int nRangeTokens = aosRangeTokens.size();
    5361         819 :             if (nRangeTokens > 3)
    5362             :             {
    5363           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
    5364             :                          pszIdxSpec);
    5365           1 :                 return nullptr;
    5366             :             }
    5367         818 :             if (nRangeTokens <= 1)
    5368             :             {
    5369           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
    5370             :                          pszIdxSpec);
    5371           1 :                 return nullptr;
    5372             :             }
    5373         817 :             const char *pszStart = aosRangeTokens[0];
    5374         817 :             const char *pszEnd = aosRangeTokens[1];
    5375         817 :             const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
    5376         817 :             GDALSlicedMDArray::Range range;
    5377         817 :             const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
    5378         817 :             range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
    5379        1633 :             if (range.m_nIncr == 0 ||
    5380         816 :                 range.m_nIncr == std::numeric_limits<GInt64>::min())
    5381             :             {
    5382           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
    5383           1 :                 return nullptr;
    5384             :             }
    5385         816 :             auto startIdx(CPLAtoGIntBig(pszStart));
    5386         816 :             if (startIdx < 0)
    5387             :             {
    5388           0 :                 if (nDimSize < static_cast<GUInt64>(-startIdx))
    5389           0 :                     startIdx = 0;
    5390             :                 else
    5391           0 :                     startIdx = nDimSize + startIdx;
    5392             :             }
    5393         816 :             const bool bPosIncr = range.m_nIncr > 0;
    5394         816 :             range.m_nStartIdx = startIdx;
    5395        1632 :             range.m_nStartIdx = EQUAL(pszStart, "")
    5396         816 :                                     ? (bPosIncr ? 0 : nDimSize - 1)
    5397             :                                     : range.m_nStartIdx;
    5398         816 :             if (range.m_nStartIdx >= nDimSize - 1)
    5399         186 :                 range.m_nStartIdx = nDimSize - 1;
    5400         816 :             auto endIdx(CPLAtoGIntBig(pszEnd));
    5401         816 :             if (endIdx < 0)
    5402             :             {
    5403           1 :                 const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
    5404           1 :                 if (nDimSize < positiveEndIdx)
    5405           0 :                     endIdx = 0;
    5406             :                 else
    5407           1 :                     endIdx = nDimSize - positiveEndIdx;
    5408             :             }
    5409         816 :             GUInt64 nEndIdx = endIdx;
    5410         816 :             nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
    5411         816 :             if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
    5412         814 :                 (!bPosIncr && range.m_nStartIdx <= nEndIdx))
    5413             :             {
    5414           3 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5415             :                          "Output dimension of size 0 is not allowed");
    5416           3 :                 return nullptr;
    5417             :             }
    5418         813 :             int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
    5419         813 :             const auto nAbsIncr = std::abs(range.m_nIncr);
    5420         813 :             const GUInt64 newSize =
    5421             :                 bPosIncr
    5422         847 :                     ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
    5423          34 :                     : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
    5424        1341 :             if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
    5425         528 :                 newSize == srcDims[nCurSrcDim]->GetSize())
    5426             :             {
    5427         159 :                 newDims.push_back(srcDims[nCurSrcDim]);
    5428             :             }
    5429             :             else
    5430             :             {
    5431         654 :                 std::string osNewDimName(srcDims[nCurSrcDim]->GetName());
    5432         654 :                 if (bRenameDimensions)
    5433             :                 {
    5434             :                     osNewDimName =
    5435        1212 :                         "subset_" + srcDims[nCurSrcDim]->GetName() +
    5436             :                         CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
    5437             :                                    "_" CPL_FRMT_GUIB,
    5438         606 :                                    static_cast<GUIntBig>(range.m_nStartIdx),
    5439         606 :                                    static_cast<GIntBig>(range.m_nIncr),
    5440         606 :                                    static_cast<GUIntBig>(newSize));
    5441             :                 }
    5442         654 :                 newDims.push_back(std::make_shared<GDALDimension>(
    5443        1308 :                     std::string(), osNewDimName, srcDims[nCurSrcDim]->GetType(),
    5444        1308 :                     range.m_nIncr > 0 ? srcDims[nCurSrcDim]->GetDirection()
    5445             :                                       : std::string(),
    5446             :                     newSize));
    5447             :             }
    5448         813 :             mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5449         813 :             parentRanges.emplace_back(range);
    5450             :         }
    5451             : 
    5452        1131 :         nCurSrcDim++;
    5453             :     }
    5454         655 :     for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
    5455             :     {
    5456          73 :         parentRanges.emplace_back(0, 1);
    5457          73 :         newDims.push_back(srcDims[nCurSrcDim]);
    5458          73 :         mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5459             :     }
    5460             : 
    5461         582 :     GDALMDArray::ViewSpec viewSpec;
    5462         582 :     viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
    5463         582 :     viewSpec.m_parentRanges = parentRanges;
    5464         582 :     viewSpecs.emplace_back(std::move(viewSpec));
    5465             : 
    5466        1164 :     return GDALSlicedMDArray::Create(self, viewExpr, std::move(newDims),
    5467         582 :                                      std::move(mapDimIdxToParentDimIdx),
    5468        1164 :                                      std::move(parentRanges));
    5469             : }
    5470             : 
    5471             : /************************************************************************/
    5472             : /*                       GDALExtractFieldMDArray                        */
    5473             : /************************************************************************/
    5474             : 
    5475             : class GDALExtractFieldMDArray final : public GDALPamMDArray
    5476             : {
    5477             :   private:
    5478             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5479             :     GDALExtendedDataType m_dt;
    5480             :     std::string m_srcCompName;
    5481             :     mutable std::vector<GByte> m_pabyNoData{};
    5482             : 
    5483             :   protected:
    5484          82 :     GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
    5485             :                             const std::string &fieldName,
    5486             :                             const std::unique_ptr<GDALEDTComponent> &srcComp)
    5487         328 :         : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
    5488         164 :                                                  " of " +
    5489          82 :                                                  poParent->GetFullName()),
    5490             :           GDALPamMDArray(
    5491         164 :               std::string(),
    5492         164 :               "Extract field " + fieldName + " of " + poParent->GetFullName(),
    5493         164 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
    5494             :           m_poParent(poParent), m_dt(srcComp->GetType()),
    5495         410 :           m_srcCompName(srcComp->GetName())
    5496             :     {
    5497          82 :         m_pabyNoData.resize(m_dt.GetSize());
    5498          82 :     }
    5499             : 
    5500             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5501             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5502             :                const GDALExtendedDataType &bufferDataType,
    5503             :                void *pDstBuffer) const override;
    5504             : 
    5505           1 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5506             :                      CSLConstList papszOptions) const override
    5507             :     {
    5508           1 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
    5509             :     }
    5510             : 
    5511             :   public:
    5512             :     static std::shared_ptr<GDALExtractFieldMDArray>
    5513          82 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5514             :            const std::string &fieldName,
    5515             :            const std::unique_ptr<GDALEDTComponent> &srcComp)
    5516             :     {
    5517             :         auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
    5518          82 :             new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
    5519          82 :         newAr->SetSelf(newAr);
    5520          82 :         return newAr;
    5521             :     }
    5522             : 
    5523         164 :     ~GDALExtractFieldMDArray()
    5524          82 :     {
    5525          82 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
    5526         164 :     }
    5527             : 
    5528          41 :     bool IsWritable() const override
    5529             :     {
    5530          41 :         return m_poParent->IsWritable();
    5531             :     }
    5532             : 
    5533         247 :     const std::string &GetFilename() const override
    5534             :     {
    5535         247 :         return m_poParent->GetFilename();
    5536             :     }
    5537             : 
    5538             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5539         343 :     GetDimensions() const override
    5540             :     {
    5541         343 :         return m_poParent->GetDimensions();
    5542             :     }
    5543             : 
    5544         289 :     const GDALExtendedDataType &GetDataType() const override
    5545             :     {
    5546         289 :         return m_dt;
    5547             :     }
    5548             : 
    5549           2 :     const std::string &GetUnit() const override
    5550             :     {
    5551           2 :         return m_poParent->GetUnit();
    5552             :     }
    5553             : 
    5554           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5555             :     {
    5556           2 :         return m_poParent->GetSpatialRef();
    5557             :     }
    5558             : 
    5559          58 :     const void *GetRawNoDataValue() const override
    5560             :     {
    5561          58 :         const void *parentNoData = m_poParent->GetRawNoDataValue();
    5562          58 :         if (parentNoData == nullptr)
    5563           1 :             return nullptr;
    5564             : 
    5565          57 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
    5566          57 :         memset(&m_pabyNoData[0], 0, m_dt.GetSize());
    5567             : 
    5568         114 :         std::vector<std::unique_ptr<GDALEDTComponent>> comps;
    5569         114 :         comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
    5570         114 :             new GDALEDTComponent(m_srcCompName, 0, m_dt)));
    5571          57 :         auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
    5572         171 :                                                 std::move(comps)));
    5573             : 
    5574          57 :         GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
    5575          57 :                                         &m_pabyNoData[0], tmpDT);
    5576             : 
    5577          57 :         return &m_pabyNoData[0];
    5578             :     }
    5579             : 
    5580           2 :     double GetOffset(bool *pbHasOffset,
    5581             :                      GDALDataType *peStorageType) const override
    5582             :     {
    5583           2 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5584             :     }
    5585             : 
    5586           2 :     double GetScale(bool *pbHasScale,
    5587             :                     GDALDataType *peStorageType) const override
    5588             :     {
    5589           2 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5590             :     }
    5591             : 
    5592          42 :     std::vector<GUInt64> GetBlockSize() const override
    5593             :     {
    5594          42 :         return m_poParent->GetBlockSize();
    5595             :     }
    5596             : };
    5597             : 
    5598             : /************************************************************************/
    5599             : /*                             IRead()                                  */
    5600             : /************************************************************************/
    5601             : 
    5602          84 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
    5603             :                                     const size_t *count,
    5604             :                                     const GInt64 *arrayStep,
    5605             :                                     const GPtrDiff_t *bufferStride,
    5606             :                                     const GDALExtendedDataType &bufferDataType,
    5607             :                                     void *pDstBuffer) const
    5608             : {
    5609         168 :     std::vector<std::unique_ptr<GDALEDTComponent>> comps;
    5610         168 :     comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
    5611         168 :         new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
    5612             :     auto tmpDT(GDALExtendedDataType::Create(
    5613         168 :         std::string(), bufferDataType.GetSize(), std::move(comps)));
    5614             : 
    5615          84 :     return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
    5616         168 :                             tmpDT, pDstBuffer);
    5617             : }
    5618             : 
    5619             : /************************************************************************/
    5620             : /*                      CreateFieldNameExtractArray()                   */
    5621             : /************************************************************************/
    5622             : 
    5623             : static std::shared_ptr<GDALMDArray>
    5624          83 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
    5625             :                             const std::string &fieldName)
    5626             : {
    5627          83 :     CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
    5628          83 :     const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
    5629         202 :     for (const auto &comp : self->GetDataType().GetComponents())
    5630             :     {
    5631         201 :         if (comp->GetName() == fieldName)
    5632             :         {
    5633          82 :             srcComp = &comp;
    5634          82 :             break;
    5635             :         }
    5636             :     }
    5637          83 :     if (srcComp == nullptr)
    5638             :     {
    5639           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
    5640             :                  fieldName.c_str());
    5641           1 :         return nullptr;
    5642             :     }
    5643          82 :     return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
    5644             : }
    5645             : 
    5646             : /************************************************************************/
    5647             : /*                             GetView()                                */
    5648             : /************************************************************************/
    5649             : 
    5650             : // clang-format off
    5651             : /** Return a view of the array using slicing or field access.
    5652             :  *
    5653             :  * The slice expression uses the same syntax as NumPy basic slicing and
    5654             :  * indexing. See
    5655             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
    5656             :  * Or it can use field access by name. See
    5657             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
    5658             :  *
    5659             :  * Multiple [] bracket elements can be concatenated, with a slice expression
    5660             :  * or field name inside each.
    5661             :  *
    5662             :  * For basic slicing and indexing, inside each [] bracket element, a list of
    5663             :  * indexes that apply to successive source dimensions, can be specified, using
    5664             :  * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
    5665             :  * or newaxis, using a comma separator.
    5666             :  *
    5667             :  * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
    5668             :  * <ul>
    5669             :  * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
    5670             :  *     at index 1 in the first dimension, and index 2 in the second dimension
    5671             :  *     from the source array. That is 5</li>
    5672             :  * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
    5673             :  * implemented internally doing this intermediate slicing approach.</li>
    5674             :  * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
    5675             :  * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
    5676             :  *     first dimension. That is [4,5,6,7].</li>
    5677             :  * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
    5678             :  *     second dimension. That is [2,6].</li>
    5679             :  * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
    5680             :  * the second dimension. That is [[2],[6]].</li>
    5681             :  * <li>GetView("[::,2]"): Same as
    5682             :  * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
    5683             :  * ellipsis only expands to one dimension here.</li>
    5684             :  * <li>GetView("[:,::2]"):
    5685             :  * returns a 2-dimensional array, with even-indexed elements of the second
    5686             :  * dimension. That is [[0,2],[4,6]].</li>
    5687             :  * <li>GetView("[:,1::2]"): returns a
    5688             :  * 2-dimensional array, with odd-indexed elements of the second dimension. That
    5689             :  * is [[1,3],[5,7]].</li>
    5690             :  * <li>GetView("[:,1:3:]"): returns a 2-dimensional
    5691             :  * array, with elements of the second dimension with index in the range [1,3[.
    5692             :  * That is [[1,2],[5,6]].</li>
    5693             :  * <li>GetView("[::-1,:]"): returns a 2-dimensional
    5694             :  * array, with the values in first dimension reversed. That is
    5695             :  * [[4,5,6,7],[0,1,2,3]].</li>
    5696             :  * <li>GetView("[newaxis,...]"): returns a
    5697             :  * 3-dimensional array, with an additional dimension of size 1 put at the
    5698             :  * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
    5699             :  * </ul>
    5700             :  *
    5701             :  * One difference with NumPy behavior is that ranges that would result in
    5702             :  * zero elements are not allowed (dimensions of size 0 not being allowed in the
    5703             :  * GDAL multidimensional model).
    5704             :  *
    5705             :  * For field access, the syntax to use is ["field_name"] or ['field_name'].
    5706             :  * Multiple field specification is not supported currently.
    5707             :  *
    5708             :  * Both type of access can be combined, e.g. GetView("[1]['field_name']")
    5709             :  *
    5710             :  * \note When using the GDAL Python bindings, natural Python syntax can be
    5711             :  * used. That is ar[0,::,1]["foo"] will be internally translated to
    5712             :  * ar.GetView("[0,::,1]['foo']")
    5713             :  * \note When using the C++ API and integer indexing only, you may use the
    5714             :  * at(idx0, idx1, ...) method.
    5715             :  *
    5716             :  * The returned array holds a reference to the original one, and thus is
    5717             :  * a view of it (not a copy). If the content of the original array changes,
    5718             :  * the content of the view array too. When using basic slicing and indexing,
    5719             :  * the view can be written if the underlying array is writable.
    5720             :  *
    5721             :  * This is the same as the C function GDALMDArrayGetView()
    5722             :  *
    5723             :  * @param viewExpr Expression expressing basic slicing and indexing, or field
    5724             :  * access.
    5725             :  * @return a new array, that holds a reference to the original one, and thus is
    5726             :  * a view of it (not a copy), or nullptr in case of error.
    5727             :  */
    5728             : // clang-format on
    5729             : 
    5730             : std::shared_ptr<GDALMDArray>
    5731         619 : GDALMDArray::GetView(const std::string &viewExpr) const
    5732             : {
    5733        1238 :     std::vector<ViewSpec> viewSpecs;
    5734        1238 :     return GetView(viewExpr, true, viewSpecs);
    5735             : }
    5736             : 
    5737             : //! @cond Doxygen_Suppress
    5738             : std::shared_ptr<GDALMDArray>
    5739         689 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
    5740             :                      std::vector<ViewSpec> &viewSpecs) const
    5741             : {
    5742        1378 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    5743         689 :     if (!self)
    5744             :     {
    5745           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    5746             :                  "Driver implementation issue: m_pSelf not set !");
    5747           1 :         return nullptr;
    5748             :     }
    5749         688 :     std::string curExpr(viewExpr);
    5750             :     while (true)
    5751             :     {
    5752         691 :         if (curExpr.empty() || curExpr[0] != '[')
    5753             :         {
    5754           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    5755             :                      "Slice string should start with ['");
    5756         688 :             return nullptr;
    5757             :         }
    5758             : 
    5759         689 :         std::string fieldName;
    5760             :         size_t endExpr;
    5761         689 :         if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
    5762             :         {
    5763          87 :             if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
    5764             :             {
    5765           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5766             :                          "Field access not allowed on non-compound data type");
    5767           2 :                 return nullptr;
    5768             :             }
    5769          85 :             size_t idx = 2;
    5770         768 :             for (; idx < curExpr.size(); idx++)
    5771             :             {
    5772         767 :                 const char ch = curExpr[idx];
    5773         767 :                 if (ch == curExpr[1])
    5774          84 :                     break;
    5775         683 :                 if (ch == '\\' && idx + 1 < curExpr.size())
    5776             :                 {
    5777           1 :                     fieldName += curExpr[idx + 1];
    5778           1 :                     idx++;
    5779             :                 }
    5780             :                 else
    5781             :                 {
    5782         682 :                     fieldName += ch;
    5783             :                 }
    5784             :             }
    5785          85 :             if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
    5786             :             {
    5787           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5788             :                          "Invalid field access specification");
    5789           2 :                 return nullptr;
    5790             :             }
    5791          83 :             endExpr = idx + 1;
    5792             :         }
    5793             :         else
    5794             :         {
    5795         602 :             endExpr = curExpr.find(']');
    5796             :         }
    5797         685 :         if (endExpr == std::string::npos)
    5798             :         {
    5799           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
    5800           1 :             return nullptr;
    5801             :         }
    5802         684 :         if (endExpr == 1)
    5803             :         {
    5804           1 :             CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
    5805           1 :             return nullptr;
    5806             :         }
    5807         683 :         std::string activeSlice(curExpr.substr(1, endExpr - 1));
    5808             : 
    5809         683 :         if (!fieldName.empty())
    5810             :         {
    5811         166 :             ViewSpec viewSpec;
    5812          83 :             viewSpec.m_osFieldName = fieldName;
    5813          83 :             viewSpecs.emplace_back(std::move(viewSpec));
    5814             :         }
    5815             : 
    5816         683 :         auto newArray = !fieldName.empty()
    5817             :                             ? CreateFieldNameExtractArray(self, fieldName)
    5818             :                             : CreateSlicedArray(self, viewExpr, activeSlice,
    5819         683 :                                                 bRenameDimensions, viewSpecs);
    5820             : 
    5821         683 :         if (endExpr == curExpr.size() - 1)
    5822             :         {
    5823         680 :             return newArray;
    5824             :         }
    5825           3 :         self = std::move(newArray);
    5826           3 :         curExpr = curExpr.substr(endExpr + 1);
    5827           3 :     }
    5828             : }
    5829             : 
    5830             : //! @endcond
    5831             : 
    5832             : std::shared_ptr<GDALMDArray>
    5833          19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
    5834             : {
    5835          19 :     std::string osExpr("[");
    5836          19 :     bool bFirst = true;
    5837          45 :     for (const auto &idx : indices)
    5838             :     {
    5839          26 :         if (!bFirst)
    5840           7 :             osExpr += ',';
    5841          26 :         bFirst = false;
    5842          26 :         osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
    5843             :     }
    5844          57 :     return GetView(osExpr + ']');
    5845             : }
    5846             : 
    5847             : /************************************************************************/
    5848             : /*                            operator[]                                */
    5849             : /************************************************************************/
    5850             : 
    5851             : /** Return a view of the array using field access
    5852             :  *
    5853             :  * Equivalent of GetView("['fieldName']")
    5854             :  *
    5855             :  * \note When operationg on a shared_ptr, use (*array)["fieldName"] syntax.
    5856             :  */
    5857             : std::shared_ptr<GDALMDArray>
    5858           2 : GDALMDArray::operator[](const std::string &fieldName) const
    5859             : {
    5860           2 :     return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
    5861           4 :                                             .replaceAll('\\', "\\\\")
    5862           4 :                                             .replaceAll('\'', "\\\'")
    5863           6 :                                             .c_str()));
    5864             : }
    5865             : 
    5866             : /************************************************************************/
    5867             : /*                      GDALMDArrayTransposed                           */
    5868             : /************************************************************************/
    5869             : 
    5870             : class GDALMDArrayTransposed final : public GDALPamMDArray
    5871             : {
    5872             :   private:
    5873             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5874             :     std::vector<int> m_anMapNewAxisToOldAxis{};
    5875             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    5876             : 
    5877             :     mutable std::vector<GUInt64> m_parentStart;
    5878             :     mutable std::vector<size_t> m_parentCount;
    5879             :     mutable std::vector<GInt64> m_parentStep;
    5880             :     mutable std::vector<GPtrDiff_t> m_parentStride;
    5881             : 
    5882             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
    5883             :                              const GInt64 *arrayStep,
    5884             :                              const GPtrDiff_t *bufferStride) const;
    5885             : 
    5886             :     static std::string
    5887          84 :     MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
    5888             :     {
    5889          84 :         std::string ret;
    5890          84 :         ret += '[';
    5891         312 :         for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
    5892             :         {
    5893         228 :             if (i > 0)
    5894         144 :                 ret += ',';
    5895         228 :             ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
    5896             :         }
    5897          84 :         ret += ']';
    5898          84 :         return ret;
    5899             :     }
    5900             : 
    5901             :   protected:
    5902          42 :     GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
    5903             :                           const std::vector<int> &anMapNewAxisToOldAxis,
    5904             :                           std::vector<std::shared_ptr<GDALDimension>> &&dims)
    5905          84 :         : GDALAbstractMDArray(std::string(),
    5906          84 :                               "Transposed view of " + poParent->GetFullName() +
    5907          84 :                                   " along " +
    5908          42 :                                   MappingToStr(anMapNewAxisToOldAxis)),
    5909          84 :           GDALPamMDArray(std::string(),
    5910          84 :                          "Transposed view of " + poParent->GetFullName() +
    5911         168 :                              " along " + MappingToStr(anMapNewAxisToOldAxis),
    5912          84 :                          GDALPamMultiDim::GetPAM(poParent),
    5913             :                          poParent->GetContext()),
    5914          42 :           m_poParent(std::move(poParent)),
    5915             :           m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
    5916          42 :           m_dims(std::move(dims)),
    5917          42 :           m_parentStart(m_poParent->GetDimensionCount()),
    5918          42 :           m_parentCount(m_poParent->GetDimensionCount()),
    5919          42 :           m_parentStep(m_poParent->GetDimensionCount()),
    5920         336 :           m_parentStride(m_poParent->GetDimensionCount())
    5921             :     {
    5922          42 :     }
    5923             : 
    5924             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5925             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5926             :                const GDALExtendedDataType &bufferDataType,
    5927             :                void *pDstBuffer) const override;
    5928             : 
    5929             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    5930             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5931             :                 const GDALExtendedDataType &bufferDataType,
    5932             :                 const void *pSrcBuffer) override;
    5933             : 
    5934             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5935             :                      CSLConstList papszOptions) const override;
    5936             : 
    5937             :   public:
    5938             :     static std::shared_ptr<GDALMDArrayTransposed>
    5939          42 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5940             :            const std::vector<int> &anMapNewAxisToOldAxis)
    5941             :     {
    5942          42 :         const auto &parentDims(poParent->GetDimensions());
    5943          84 :         std::vector<std::shared_ptr<GDALDimension>> dims;
    5944         156 :         for (const auto iOldAxis : anMapNewAxisToOldAxis)
    5945             :         {
    5946         114 :             if (iOldAxis < 0)
    5947             :             {
    5948           1 :                 dims.push_back(std::make_shared<GDALDimension>(
    5949           2 :                     std::string(), "newaxis", std::string(), std::string(), 1));
    5950             :             }
    5951             :             else
    5952             :             {
    5953         113 :                 dims.emplace_back(parentDims[iOldAxis]);
    5954             :             }
    5955             :         }
    5956             : 
    5957             :         auto newAr(
    5958             :             std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
    5959          42 :                 poParent, anMapNewAxisToOldAxis, std::move(dims))));
    5960          42 :         newAr->SetSelf(newAr);
    5961          84 :         return newAr;
    5962             :     }
    5963             : 
    5964           1 :     bool IsWritable() const override
    5965             :     {
    5966           1 :         return m_poParent->IsWritable();
    5967             :     }
    5968             : 
    5969          84 :     const std::string &GetFilename() const override
    5970             :     {
    5971          84 :         return m_poParent->GetFilename();
    5972             :     }
    5973             : 
    5974             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5975         358 :     GetDimensions() const override
    5976             :     {
    5977         358 :         return m_dims;
    5978             :     }
    5979             : 
    5980         141 :     const GDALExtendedDataType &GetDataType() const override
    5981             :     {
    5982         141 :         return m_poParent->GetDataType();
    5983             :     }
    5984             : 
    5985           4 :     const std::string &GetUnit() const override
    5986             :     {
    5987           4 :         return m_poParent->GetUnit();
    5988             :     }
    5989             : 
    5990           5 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5991             :     {
    5992          10 :         auto poSrcSRS = m_poParent->GetSpatialRef();
    5993           5 :         if (!poSrcSRS)
    5994           2 :             return nullptr;
    5995           6 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
    5996           6 :         std::vector<int> dstMapping;
    5997           9 :         for (int srcAxis : srcMapping)
    5998             :         {
    5999           6 :             bool bFound = false;
    6000          14 :             for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
    6001             :             {
    6002          14 :                 if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
    6003             :                 {
    6004           6 :                     dstMapping.push_back(static_cast<int>(i) + 1);
    6005           6 :                     bFound = true;
    6006           6 :                     break;
    6007             :                 }
    6008             :             }
    6009           6 :             if (!bFound)
    6010             :             {
    6011           0 :                 dstMapping.push_back(0);
    6012             :             }
    6013             :         }
    6014           6 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
    6015           3 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
    6016           3 :         return poClone;
    6017             :     }
    6018             : 
    6019           4 :     const void *GetRawNoDataValue() const override
    6020             :     {
    6021           4 :         return m_poParent->GetRawNoDataValue();
    6022             :     }
    6023             : 
    6024             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
    6025             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
    6026             : 
    6027           4 :     double GetOffset(bool *pbHasOffset,
    6028             :                      GDALDataType *peStorageType) const override
    6029             :     {
    6030           4 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    6031             :     }
    6032             : 
    6033           4 :     double GetScale(bool *pbHasScale,
    6034             :                     GDALDataType *peStorageType) const override
    6035             :     {
    6036           4 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    6037             :     }
    6038             : 
    6039             :     // bool SetOffset(double dfOffset) override { return
    6040             :     // m_poParent->SetOffset(dfOffset); }
    6041             : 
    6042             :     // bool SetScale(double dfScale) override { return
    6043             :     // m_poParent->SetScale(dfScale); }
    6044             : 
    6045           3 :     std::vector<GUInt64> GetBlockSize() const override
    6046             :     {
    6047           3 :         std::vector<GUInt64> ret(GetDimensionCount());
    6048           6 :         const auto parentBlockSize(m_poParent->GetBlockSize());
    6049          11 :         for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
    6050             :         {
    6051           8 :             const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
    6052           8 :             if (iOldAxis >= 0)
    6053             :             {
    6054           7 :                 ret[i] = parentBlockSize[iOldAxis];
    6055             :             }
    6056             :         }
    6057           6 :         return ret;
    6058             :     }
    6059             : 
    6060             :     std::shared_ptr<GDALAttribute>
    6061           1 :     GetAttribute(const std::string &osName) const override
    6062             :     {
    6063           1 :         return m_poParent->GetAttribute(osName);
    6064             :     }
    6065             : 
    6066             :     std::vector<std::shared_ptr<GDALAttribute>>
    6067           6 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    6068             :     {
    6069           6 :         return m_poParent->GetAttributes(papszOptions);
    6070             :     }
    6071             : };
    6072             : 
    6073             : /************************************************************************/
    6074             : /*                         PrepareParentArrays()                        */
    6075             : /************************************************************************/
    6076             : 
    6077          47 : void GDALMDArrayTransposed::PrepareParentArrays(
    6078             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    6079             :     const GPtrDiff_t *bufferStride) const
    6080             : {
    6081         176 :     for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
    6082             :     {
    6083         129 :         const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
    6084         129 :         if (iOldAxis >= 0)
    6085             :         {
    6086         128 :             m_parentStart[iOldAxis] = arrayStartIdx[i];
    6087         128 :             m_parentCount[iOldAxis] = count[i];
    6088         128 :             if (arrayStep)  // only null when called from IAdviseRead()
    6089             :             {
    6090         126 :                 m_parentStep[iOldAxis] = arrayStep[i];
    6091             :             }
    6092         128 :             if (bufferStride)  // only null when called from IAdviseRead()
    6093             :             {
    6094         126 :                 m_parentStride[iOldAxis] = bufferStride[i];
    6095             :             }
    6096             :         }
    6097             :     }
    6098          47 : }
    6099             : 
    6100             : /************************************************************************/
    6101             : /*                             IRead()                                  */
    6102             : /************************************************************************/
    6103             : 
    6104          44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
    6105             :                                   const size_t *count, const GInt64 *arrayStep,
    6106             :                                   const GPtrDiff_t *bufferStride,
    6107             :                                   const GDALExtendedDataType &bufferDataType,
    6108             :                                   void *pDstBuffer) const
    6109             : {
    6110          44 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    6111          88 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
    6112          44 :                             m_parentStep.data(), m_parentStride.data(),
    6113          44 :                             bufferDataType, pDstBuffer);
    6114             : }
    6115             : 
    6116             : /************************************************************************/
    6117             : /*                            IWrite()                                  */
    6118             : /************************************************************************/
    6119             : 
    6120           2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
    6121             :                                    const size_t *count, const GInt64 *arrayStep,
    6122             :                                    const GPtrDiff_t *bufferStride,
    6123             :                                    const GDALExtendedDataType &bufferDataType,
    6124             :                                    const void *pSrcBuffer)
    6125             : {
    6126           2 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    6127           4 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
    6128           2 :                              m_parentStep.data(), m_parentStride.data(),
    6129           2 :                              bufferDataType, pSrcBuffer);
    6130             : }
    6131             : 
    6132             : /************************************************************************/
    6133             : /*                             IAdviseRead()                            */
    6134             : /************************************************************************/
    6135             : 
    6136           1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
    6137             :                                         const size_t *count,
    6138             :                                         CSLConstList papszOptions) const
    6139             : {
    6140           1 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
    6141           1 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
    6142           1 :                                   papszOptions);
    6143             : }
    6144             : 
    6145             : /************************************************************************/
    6146             : /*                           Transpose()                                */
    6147             : /************************************************************************/
    6148             : 
    6149             : /** Return a view of the array whose axis have been reordered.
    6150             :  *
    6151             :  * The anMapNewAxisToOldAxis parameter should contain all the values between 0
    6152             :  * and GetDimensionCount() - 1, and each only once.
    6153             :  * -1 can be used as a special index value to ask for the insertion of a new
    6154             :  * axis of size 1.
    6155             :  * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
    6156             :  * index of one of its dimension, it corresponds to the axis of index
    6157             :  * anMapNewAxisToOldAxis[i] from the current array.
    6158             :  *
    6159             :  * This is similar to the numpy.transpose() method
    6160             :  *
    6161             :  * The returned array holds a reference to the original one, and thus is
    6162             :  * a view of it (not a copy). If the content of the original array changes,
    6163             :  * the content of the view array too. The view can be written if the underlying
    6164             :  * array is writable.
    6165             :  *
    6166             :  * Note that I/O performance in such a transposed view might be poor.
    6167             :  *
    6168             :  * This is the same as the C function GDALMDArrayTranspose().
    6169             :  *
    6170             :  * @return a new array, that holds a reference to the original one, and thus is
    6171             :  * a view of it (not a copy), or nullptr in case of error.
    6172             :  */
    6173             : std::shared_ptr<GDALMDArray>
    6174          50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
    6175             : {
    6176         100 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    6177          50 :     if (!self)
    6178             :     {
    6179           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6180             :                  "Driver implementation issue: m_pSelf not set !");
    6181           0 :         return nullptr;
    6182             :     }
    6183          50 :     const int nDims = static_cast<int>(GetDimensionCount());
    6184         100 :     std::vector<bool> alreadyUsedOldAxis(nDims, false);
    6185          50 :     int nCountOldAxis = 0;
    6186         179 :     for (const auto iOldAxis : anMapNewAxisToOldAxis)
    6187             :     {
    6188         133 :         if (iOldAxis < -1 || iOldAxis >= nDims)
    6189             :         {
    6190           3 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
    6191           4 :             return nullptr;
    6192             :         }
    6193         130 :         if (iOldAxis >= 0)
    6194             :         {
    6195         128 :             if (alreadyUsedOldAxis[iOldAxis])
    6196             :             {
    6197           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
    6198             :                          iOldAxis);
    6199           1 :                 return nullptr;
    6200             :             }
    6201         127 :             alreadyUsedOldAxis[iOldAxis] = true;
    6202         127 :             nCountOldAxis++;
    6203             :         }
    6204             :     }
    6205          46 :     if (nCountOldAxis != nDims)
    6206             :     {
    6207           4 :         CPLError(CE_Failure, CPLE_AppDefined,
    6208             :                  "One or several original axis missing");
    6209           4 :         return nullptr;
    6210             :     }
    6211          42 :     return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
    6212             : }
    6213             : 
    6214             : /************************************************************************/
    6215             : /*                             IRead()                                  */
    6216             : /************************************************************************/
    6217             : 
    6218          16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
    6219             :                                 const size_t *count, const GInt64 *arrayStep,
    6220             :                                 const GPtrDiff_t *bufferStride,
    6221             :                                 const GDALExtendedDataType &bufferDataType,
    6222             :                                 void *pDstBuffer) const
    6223             : {
    6224          16 :     const double dfScale = m_dfScale;
    6225          16 :     const double dfOffset = m_dfOffset;
    6226          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
    6227             :     const auto dtDouble =
    6228          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
    6229          16 :     const size_t nDTSize = dtDouble.GetSize();
    6230          16 :     const bool bTempBufferNeeded = (dtDouble != bufferDataType);
    6231             : 
    6232          16 :     double adfSrcNoData[2] = {0, 0};
    6233          16 :     if (m_bHasNoData)
    6234             :     {
    6235           9 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
    6236           9 :                                         m_poParent->GetDataType(),
    6237             :                                         &adfSrcNoData[0], dtDouble);
    6238             :     }
    6239             : 
    6240          16 :     const auto nDims = GetDimensions().size();
    6241          16 :     if (nDims == 0)
    6242             :     {
    6243             :         double adfVal[2];
    6244           9 :         if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
    6245             :                               dtDouble, &adfVal[0]))
    6246             :         {
    6247           0 :             return false;
    6248             :         }
    6249           9 :         if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
    6250             :         {
    6251           6 :             adfVal[0] = adfVal[0] * dfScale + dfOffset;
    6252           6 :             if (bDTIsComplex)
    6253             :             {
    6254           2 :                 adfVal[1] = adfVal[1] * dfScale + dfOffset;
    6255             :             }
    6256           6 :             GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
    6257             :                                             bufferDataType);
    6258             :         }
    6259             :         else
    6260             :         {
    6261           3 :             GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
    6262             :                                             pDstBuffer, bufferDataType);
    6263             :         }
    6264           9 :         return true;
    6265             :     }
    6266             : 
    6267          14 :     std::vector<GPtrDiff_t> actualBufferStrideVector;
    6268           7 :     const GPtrDiff_t *actualBufferStridePtr = bufferStride;
    6269           7 :     void *pTempBuffer = pDstBuffer;
    6270           7 :     if (bTempBufferNeeded)
    6271             :     {
    6272           2 :         size_t nElts = 1;
    6273           2 :         actualBufferStrideVector.resize(nDims);
    6274           7 :         for (size_t i = 0; i < nDims; i++)
    6275           5 :             nElts *= count[i];
    6276           2 :         actualBufferStrideVector.back() = 1;
    6277           5 :         for (size_t i = nDims - 1; i > 0;)
    6278             :         {
    6279           3 :             --i;
    6280           3 :             actualBufferStrideVector[i] =
    6281           3 :                 actualBufferStrideVector[i + 1] * count[i + 1];
    6282             :         }
    6283           2 :         actualBufferStridePtr = actualBufferStrideVector.data();
    6284           2 :         pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
    6285           2 :         if (!pTempBuffer)
    6286           0 :             return false;
    6287             :     }
    6288           7 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
    6289             :                           actualBufferStridePtr, dtDouble, pTempBuffer))
    6290             :     {
    6291           0 :         if (bTempBufferNeeded)
    6292           0 :             VSIFree(pTempBuffer);
    6293           0 :         return false;
    6294             :     }
    6295             : 
    6296             :     struct Stack
    6297             :     {
    6298             :         size_t nIters = 0;
    6299             :         double *src_ptr = nullptr;
    6300             :         GByte *dst_ptr = nullptr;
    6301             :         GPtrDiff_t src_inc_offset = 0;
    6302             :         GPtrDiff_t dst_inc_offset = 0;
    6303             :     };
    6304             : 
    6305           7 :     std::vector<Stack> stack(nDims);
    6306           7 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    6307          23 :     for (size_t i = 0; i < nDims; i++)
    6308             :     {
    6309          32 :         stack[i].src_inc_offset =
    6310          16 :             actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
    6311          16 :         stack[i].dst_inc_offset =
    6312          16 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6313             :     }
    6314           7 :     stack[0].src_ptr = static_cast<double *>(pTempBuffer);
    6315           7 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    6316             : 
    6317           7 :     size_t dimIdx = 0;
    6318           7 :     const size_t nDimsMinus1 = nDims - 1;
    6319             :     GByte abyDstNoData[16];
    6320           7 :     CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
    6321           7 :     GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
    6322             :                                     bufferDataType);
    6323             : 
    6324          37 : lbl_next_depth:
    6325          37 :     if (dimIdx == nDimsMinus1)
    6326             :     {
    6327          25 :         auto nIters = count[dimIdx];
    6328          25 :         double *padfVal = stack[dimIdx].src_ptr;
    6329          25 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
    6330             :         while (true)
    6331             :         {
    6332          92 :             if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
    6333             :             {
    6334          88 :                 padfVal[0] = padfVal[0] * dfScale + dfOffset;
    6335          88 :                 if (bDTIsComplex)
    6336             :                 {
    6337           1 :                     padfVal[1] = padfVal[1] * dfScale + dfOffset;
    6338             :                 }
    6339          88 :                 if (bTempBufferNeeded)
    6340             :                 {
    6341          29 :                     GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
    6342             :                                                     dst_ptr, bufferDataType);
    6343             :                 }
    6344             :             }
    6345             :             else
    6346             :             {
    6347           4 :                 memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
    6348             :             }
    6349             : 
    6350          92 :             if ((--nIters) == 0)
    6351          25 :                 break;
    6352          67 :             padfVal += stack[dimIdx].src_inc_offset;
    6353          67 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    6354             :         }
    6355             :     }
    6356             :     else
    6357             :     {
    6358          12 :         stack[dimIdx].nIters = count[dimIdx];
    6359             :         while (true)
    6360             :         {
    6361          30 :             dimIdx++;
    6362          30 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    6363          30 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6364          30 :             goto lbl_next_depth;
    6365          30 :         lbl_return_to_caller:
    6366          30 :             dimIdx--;
    6367          30 :             if ((--stack[dimIdx].nIters) == 0)
    6368          12 :                 break;
    6369          18 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    6370          18 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6371             :         }
    6372             :     }
    6373          37 :     if (dimIdx > 0)
    6374          30 :         goto lbl_return_to_caller;
    6375             : 
    6376           7 :     if (bTempBufferNeeded)
    6377           2 :         VSIFree(pTempBuffer);
    6378           7 :     return true;
    6379             : }
    6380             : 
    6381             : /************************************************************************/
    6382             : /*                             IWrite()                                 */
    6383             : /************************************************************************/
    6384             : 
    6385          16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
    6386             :                                  const size_t *count, const GInt64 *arrayStep,
    6387             :                                  const GPtrDiff_t *bufferStride,
    6388             :                                  const GDALExtendedDataType &bufferDataType,
    6389             :                                  const void *pSrcBuffer)
    6390             : {
    6391          16 :     const double dfScale = m_dfScale;
    6392          16 :     const double dfOffset = m_dfOffset;
    6393          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
    6394             :     const auto dtDouble =
    6395          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
    6396          16 :     const size_t nDTSize = dtDouble.GetSize();
    6397          16 :     const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
    6398             :     const bool bSelfAndParentHaveNoData =
    6399          16 :         m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
    6400          16 :     double dfNoData = 0;
    6401          16 :     if (m_bHasNoData)
    6402             :     {
    6403           7 :         GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
    6404             :                         &dfNoData, GDT_Float64, 0, 1);
    6405             :     }
    6406             : 
    6407          16 :     double adfSrcNoData[2] = {0, 0};
    6408          16 :     if (bSelfAndParentHaveNoData)
    6409             :     {
    6410           7 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
    6411           7 :                                         m_poParent->GetDataType(),
    6412             :                                         &adfSrcNoData[0], dtDouble);
    6413             :     }
    6414             : 
    6415          16 :     const auto nDims = GetDimensions().size();
    6416          16 :     if (nDims == 0)
    6417             :     {
    6418             :         double adfVal[2];
    6419          10 :         GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
    6420             :                                         dtDouble);
    6421          16 :         if (bSelfAndParentHaveNoData &&
    6422           6 :             (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
    6423             :         {
    6424           4 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
    6425           2 :                                      bufferStride, m_poParent->GetDataType(),
    6426           4 :                                      m_poParent->GetRawNoDataValue());
    6427             :         }
    6428             :         else
    6429             :         {
    6430           8 :             adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
    6431           8 :             if (bDTIsComplex)
    6432             :             {
    6433           2 :                 adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
    6434             :             }
    6435           8 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
    6436           8 :                                      bufferStride, dtDouble, &adfVal[0]);
    6437             :         }
    6438             :     }
    6439             : 
    6440          12 :     std::vector<GPtrDiff_t> tmpBufferStrideVector;
    6441           6 :     size_t nElts = 1;
    6442           6 :     tmpBufferStrideVector.resize(nDims);
    6443          20 :     for (size_t i = 0; i < nDims; i++)
    6444          14 :         nElts *= count[i];
    6445           6 :     tmpBufferStrideVector.back() = 1;
    6446          14 :     for (size_t i = nDims - 1; i > 0;)
    6447             :     {
    6448           8 :         --i;
    6449           8 :         tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
    6450             :     }
    6451           6 :     const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
    6452           6 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
    6453           6 :     if (!pTempBuffer)
    6454           0 :         return false;
    6455             : 
    6456             :     struct Stack
    6457             :     {
    6458             :         size_t nIters = 0;
    6459             :         double *dst_ptr = nullptr;
    6460             :         const GByte *src_ptr = nullptr;
    6461             :         GPtrDiff_t src_inc_offset = 0;
    6462             :         GPtrDiff_t dst_inc_offset = 0;
    6463             :     };
    6464             : 
    6465           6 :     std::vector<Stack> stack(nDims);
    6466           6 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    6467          20 :     for (size_t i = 0; i < nDims; i++)
    6468             :     {
    6469          28 :         stack[i].dst_inc_offset =
    6470          14 :             tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
    6471          14 :         stack[i].src_inc_offset =
    6472          14 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6473             :     }
    6474           6 :     stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
    6475           6 :     stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
    6476             : 
    6477           6 :     size_t dimIdx = 0;
    6478           6 :     const size_t nDimsMinus1 = nDims - 1;
    6479             : 
    6480          34 : lbl_next_depth:
    6481          34 :     if (dimIdx == nDimsMinus1)
    6482             :     {
    6483          23 :         auto nIters = count[dimIdx];
    6484          23 :         double *dst_ptr = stack[dimIdx].dst_ptr;
    6485          23 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
    6486             :         while (true)
    6487             :         {
    6488             :             double adfVal[2];
    6489             :             const double *padfSrcVal;
    6490          86 :             if (bIsBufferDataTypeNativeDataType)
    6491             :             {
    6492          50 :                 padfSrcVal = reinterpret_cast<const double *>(src_ptr);
    6493             :             }
    6494             :             else
    6495             :             {
    6496          36 :                 GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
    6497             :                                                 &adfVal[0], dtDouble);
    6498          36 :                 padfSrcVal = adfVal;
    6499             :             }
    6500             : 
    6501         148 :             if (bSelfAndParentHaveNoData &&
    6502          62 :                 (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
    6503             :             {
    6504           3 :                 dst_ptr[0] = adfSrcNoData[0];
    6505           3 :                 if (bDTIsComplex)
    6506             :                 {
    6507           1 :                     dst_ptr[1] = adfSrcNoData[1];
    6508             :                 }
    6509             :             }
    6510             :             else
    6511             :             {
    6512          83 :                 dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
    6513          83 :                 if (bDTIsComplex)
    6514             :                 {
    6515           1 :                     dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
    6516             :                 }
    6517             :             }
    6518             : 
    6519          86 :             if ((--nIters) == 0)
    6520          23 :                 break;
    6521          63 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    6522          63 :             src_ptr += stack[dimIdx].src_inc_offset;
    6523          63 :         }
    6524             :     }
    6525             :     else
    6526             :     {
    6527          11 :         stack[dimIdx].nIters = count[dimIdx];
    6528             :         while (true)
    6529             :         {
    6530          28 :             dimIdx++;
    6531          28 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    6532          28 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6533          28 :             goto lbl_next_depth;
    6534          28 :         lbl_return_to_caller:
    6535          28 :             dimIdx--;
    6536          28 :             if ((--stack[dimIdx].nIters) == 0)
    6537          11 :                 break;
    6538          17 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    6539          17 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6540             :         }
    6541             :     }
    6542          34 :     if (dimIdx > 0)
    6543          28 :         goto lbl_return_to_caller;
    6544             : 
    6545             :     // If the parent array is not double/complex-double, then convert the
    6546             :     // values to it, before calling Write(), as some implementations can be
    6547             :     // very slow when doing the type conversion.
    6548           6 :     const auto &eParentDT = m_poParent->GetDataType();
    6549           6 :     const size_t nParentDTSize = eParentDT.GetSize();
    6550           6 :     if (nParentDTSize <= nDTSize / 2)
    6551             :     {
    6552             :         // Copy in-place by making sure that source and target do not overlap
    6553           6 :         const auto eNumericDT = dtDouble.GetNumericDataType();
    6554           6 :         const auto eParentNumericDT = eParentDT.GetNumericDataType();
    6555             : 
    6556             :         // Copy first element
    6557             :         {
    6558           6 :             std::vector<GByte> abyTemp(nParentDTSize);
    6559           6 :             GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
    6560           6 :                             static_cast<int>(nDTSize), &abyTemp[0],
    6561             :                             eParentNumericDT, static_cast<int>(nParentDTSize),
    6562             :                             1);
    6563           6 :             memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
    6564             :         }
    6565             :         // Remaining elements
    6566          86 :         for (size_t i = 1; i < nElts; ++i)
    6567             :         {
    6568          80 :             GDALCopyWords64(
    6569          80 :                 static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
    6570          80 :                 static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
    6571             :                 eParentNumericDT, 0, 1);
    6572             :         }
    6573             :     }
    6574             : 
    6575             :     const bool ret =
    6576           6 :         m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
    6577             :                           eParentDT, pTempBuffer);
    6578             : 
    6579           6 :     VSIFree(pTempBuffer);
    6580           6 :     return ret;
    6581             : }
    6582             : 
    6583             : /************************************************************************/
    6584             : /*                           GetUnscaled()                              */
    6585             : /************************************************************************/
    6586             : 
    6587             : /** Return an array that is the unscaled version of the current one.
    6588             :  *
    6589             :  * That is each value of the unscaled array will be
    6590             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    6591             :  *
    6592             :  * Starting with GDAL 3.3, the Write() method is implemented and will convert
    6593             :  * from unscaled values to raw values.
    6594             :  *
    6595             :  * This is the same as the C function GDALMDArrayGetUnscaled().
    6596             :  *
    6597             :  * @param dfOverriddenScale Custom scale value instead of GetScale()
    6598             :  * @param dfOverriddenOffset Custom offset value instead of GetOffset()
    6599             :  * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
    6600             :  * @return a new array, that holds a reference to the original one, and thus is
    6601             :  * a view of it (not a copy), or nullptr in case of error.
    6602             :  */
    6603             : std::shared_ptr<GDALMDArray>
    6604          17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
    6605             :                          double dfOverriddenDstNodata) const
    6606             : {
    6607          34 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    6608          17 :     if (!self)
    6609             :     {
    6610           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6611             :                  "Driver implementation issue: m_pSelf not set !");
    6612           0 :         return nullptr;
    6613             :     }
    6614          17 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    6615             :     {
    6616           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6617             :                  "GetUnscaled() only supports numeric data type");
    6618           0 :         return nullptr;
    6619             :     }
    6620             :     const double dfScale =
    6621          17 :         std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
    6622             :     const double dfOffset =
    6623          17 :         std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
    6624          17 :     if (dfScale == 1.0 && dfOffset == 0.0)
    6625           4 :         return self;
    6626             : 
    6627          13 :     GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
    6628          13 :                            ? GDT_CFloat64
    6629          13 :                            : GDT_Float64;
    6630          13 :     if (dfOverriddenScale == -1 && dfOverriddenOffset == 0)
    6631             :     {
    6632           1 :         if (GetDataType().GetNumericDataType() == GDT_Float16)
    6633           0 :             eDT = GDT_Float16;
    6634           1 :         if (GetDataType().GetNumericDataType() == GDT_Float32)
    6635           1 :             eDT = GDT_Float32;
    6636             :     }
    6637             : 
    6638          26 :     return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
    6639          13 :                                        dfOverriddenDstNodata, eDT);
    6640             : }
    6641             : 
    6642             : /************************************************************************/
    6643             : /*                         GDALMDArrayMask                              */
    6644             : /************************************************************************/
    6645             : 
    6646             : class GDALMDArrayMask final : public GDALPamMDArray
    6647             : {
    6648             :   private:
    6649             :     std::shared_ptr<GDALMDArray> m_poParent{};
    6650             :     GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_Byte)};
    6651             :     double m_dfMissingValue = 0.0;
    6652             :     bool m_bHasMissingValue = false;
    6653             :     double m_dfFillValue = 0.0;
    6654             :     bool m_bHasFillValue = false;
    6655             :     double m_dfValidMin = 0.0;
    6656             :     bool m_bHasValidMin = false;
    6657             :     double m_dfValidMax = 0.0;
    6658             :     bool m_bHasValidMax = false;
    6659             :     std::vector<uint32_t> m_anValidFlagMasks{};
    6660             :     std::vector<uint32_t> m_anValidFlagValues{};
    6661             : 
    6662             :     bool Init(CSLConstList papszOptions);
    6663             : 
    6664             :     template <typename Type>
    6665             :     void
    6666             :     ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
    6667             :                  const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    6668             :                  const void *pTempBuffer,
    6669             :                  const GDALExtendedDataType &oTmpBufferDT,
    6670             :                  const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
    6671             : 
    6672             :   protected:
    6673          48 :     explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
    6674          96 :         : GDALAbstractMDArray(std::string(),
    6675          96 :                               "Mask of " + poParent->GetFullName()),
    6676          96 :           GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
    6677          96 :                          GDALPamMultiDim::GetPAM(poParent),
    6678             :                          poParent->GetContext()),
    6679         240 :           m_poParent(std::move(poParent))
    6680             :     {
    6681          48 :     }
    6682             : 
    6683             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6684             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    6685             :                const GDALExtendedDataType &bufferDataType,
    6686             :                void *pDstBuffer) const override;
    6687             : 
    6688           0 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6689             :                      CSLConstList papszOptions) const override
    6690             :     {
    6691           0 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
    6692             :     }
    6693             : 
    6694             :   public:
    6695             :     static std::shared_ptr<GDALMDArrayMask>
    6696             :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    6697             :            CSLConstList papszOptions);
    6698             : 
    6699           1 :     bool IsWritable() const override
    6700             :     {
    6701           1 :         return false;
    6702             :     }
    6703             : 
    6704          54 :     const std::string &GetFilename() const override
    6705             :     {
    6706          54 :         return m_poParent->GetFilename();
    6707             :     }
    6708             : 
    6709             :     const std::vector<std::shared_ptr<GDALDimension>> &
    6710         382 :     GetDimensions() const override
    6711             :     {
    6712         382 :         return m_poParent->GetDimensions();
    6713             :     }
    6714             : 
    6715         138 :     const GDALExtendedDataType &GetDataType() const override
    6716             :     {
    6717         138 :         return m_dt;
    6718             :     }
    6719             : 
    6720           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    6721             :     {
    6722           1 :         return m_poParent->GetSpatialRef();
    6723             :     }
    6724             : 
    6725           2 :     std::vector<GUInt64> GetBlockSize() const override
    6726             :     {
    6727           2 :         return m_poParent->GetBlockSize();
    6728             :     }
    6729             : };
    6730             : 
    6731             : /************************************************************************/
    6732             : /*                    GDALMDArrayMask::Create()                         */
    6733             : /************************************************************************/
    6734             : 
    6735             : /* static */ std::shared_ptr<GDALMDArrayMask>
    6736          48 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
    6737             :                         CSLConstList papszOptions)
    6738             : {
    6739          96 :     auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
    6740          48 :     newAr->SetSelf(newAr);
    6741          48 :     if (!newAr->Init(papszOptions))
    6742           6 :         return nullptr;
    6743          42 :     return newAr;
    6744             : }
    6745             : 
    6746             : /************************************************************************/
    6747             : /*                    GDALMDArrayMask::Init()                           */
    6748             : /************************************************************************/
    6749             : 
    6750          48 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
    6751             : {
    6752             :     const auto GetSingleValNumericAttr =
    6753         192 :         [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
    6754             :     {
    6755         576 :         auto poAttr = m_poParent->GetAttribute(pszAttrName);
    6756         192 :         if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
    6757             :         {
    6758          22 :             const auto anDimSizes = poAttr->GetDimensionsSize();
    6759          21 :             if (anDimSizes.empty() ||
    6760          10 :                 (anDimSizes.size() == 1 && anDimSizes[0] == 1))
    6761             :             {
    6762          11 :                 bHasVal = true;
    6763          11 :                 dfVal = poAttr->ReadAsDouble();
    6764             :             }
    6765             :         }
    6766         192 :     };
    6767             : 
    6768          48 :     GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
    6769          48 :                             m_dfMissingValue);
    6770          48 :     GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
    6771          48 :     GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
    6772          48 :     GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
    6773             : 
    6774             :     {
    6775         144 :         auto poValidRange = m_poParent->GetAttribute("valid_range");
    6776          54 :         if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
    6777          60 :             poValidRange->GetDimensionsSize()[0] == 2 &&
    6778           6 :             poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
    6779             :         {
    6780           6 :             m_bHasValidMin = true;
    6781           6 :             m_bHasValidMax = true;
    6782           6 :             auto vals = poValidRange->ReadAsDoubleArray();
    6783           6 :             CPLAssert(vals.size() == 2);
    6784           6 :             m_dfValidMin = vals[0];
    6785           6 :             m_dfValidMax = vals[1];
    6786             :         }
    6787             :     }
    6788             : 
    6789             :     // Take into account
    6790             :     // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
    6791             :     // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
    6792             :     const char *pszUnmaskFlags =
    6793          48 :         CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
    6794          48 :     if (pszUnmaskFlags)
    6795             :     {
    6796             :         const auto IsScalarStringAttr =
    6797          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
    6798             :         {
    6799          26 :             return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
    6800          26 :                    (poAttr->GetDimensionsSize().empty() ||
    6801          13 :                     (poAttr->GetDimensionsSize().size() == 1 &&
    6802          26 :                      poAttr->GetDimensionsSize()[0] == 1));
    6803             :         };
    6804             : 
    6805          28 :         auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
    6806          14 :         if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
    6807             :         {
    6808           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6809             :                      "UNMASK_FLAGS option specified but array has no "
    6810             :                      "flag_meanings attribute");
    6811           1 :             return false;
    6812             :         }
    6813          13 :         const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
    6814          13 :         if (!pszFlagMeanings)
    6815             :         {
    6816           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6817             :                      "Cannot read flag_meanings attribute");
    6818           1 :             return false;
    6819             :         }
    6820             : 
    6821             :         const auto IsSingleDimNumericAttr =
    6822          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
    6823             :         {
    6824          26 :             return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
    6825          26 :                    poAttr->GetDimensionsSize().size() == 1;
    6826             :         };
    6827             : 
    6828          24 :         auto poFlagValues = m_poParent->GetAttribute("flag_values");
    6829             :         const bool bHasFlagValues =
    6830          12 :             poFlagValues && IsSingleDimNumericAttr(poFlagValues);
    6831             : 
    6832          24 :         auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
    6833             :         const bool bHasFlagMasks =
    6834          12 :             poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
    6835             : 
    6836          12 :         if (!bHasFlagValues && !bHasFlagMasks)
    6837             :         {
    6838           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6839             :                      "Cannot find flag_values and/or flag_masks attribute");
    6840           1 :             return false;
    6841             :         }
    6842             : 
    6843             :         const CPLStringList aosUnmaskFlags(
    6844          11 :             CSLTokenizeString2(pszUnmaskFlags, ",", 0));
    6845             :         const CPLStringList aosFlagMeanings(
    6846          11 :             CSLTokenizeString2(pszFlagMeanings, " ", 0));
    6847             : 
    6848          11 :         if (bHasFlagValues)
    6849             :         {
    6850           7 :             const auto eType = poFlagValues->GetDataType().GetNumericDataType();
    6851             :             // We could support Int64 or UInt64, but more work...
    6852           7 :             if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
    6853           0 :                 eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
    6854             :             {
    6855           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    6856             :                          "Unsupported data type for flag_values attribute: %s",
    6857             :                          GDALGetDataTypeName(eType));
    6858           0 :                 return false;
    6859             :             }
    6860             :         }
    6861             : 
    6862          11 :         if (bHasFlagMasks)
    6863             :         {
    6864           6 :             const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
    6865             :             // We could support Int64 or UInt64, but more work...
    6866           6 :             if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
    6867           0 :                 eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
    6868             :             {
    6869           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    6870             :                          "Unsupported data type for flag_masks attribute: %s",
    6871             :                          GDALGetDataTypeName(eType));
    6872           0 :                 return false;
    6873             :             }
    6874             :         }
    6875             : 
    6876             :         const std::vector<double> adfValues(
    6877             :             bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
    6878          11 :                            : std::vector<double>());
    6879             :         const std::vector<double> adfMasks(
    6880             :             bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
    6881          11 :                           : std::vector<double>());
    6882             : 
    6883          18 :         if (bHasFlagValues &&
    6884           7 :             adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
    6885             :         {
    6886           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6887             :                      "Number of values in flag_values attribute is different "
    6888             :                      "from the one in flag_meanings");
    6889           1 :             return false;
    6890             :         }
    6891             : 
    6892          16 :         if (bHasFlagMasks &&
    6893           6 :             adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
    6894             :         {
    6895           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6896             :                      "Number of values in flag_masks attribute is different "
    6897             :                      "from the one in flag_meanings");
    6898           1 :             return false;
    6899             :         }
    6900             : 
    6901          19 :         for (int i = 0; i < aosUnmaskFlags.size(); ++i)
    6902             :         {
    6903          11 :             const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
    6904          11 :             if (nIdxFlag < 0)
    6905             :             {
    6906           1 :                 CPLError(
    6907             :                     CE_Failure, CPLE_AppDefined,
    6908             :                     "Cannot fing flag %s in flag_meanings = '%s' attribute",
    6909             :                     aosUnmaskFlags[i], pszFlagMeanings);
    6910           1 :                 return false;
    6911             :             }
    6912             : 
    6913          10 :             if (bHasFlagValues && adfValues[nIdxFlag] < 0)
    6914             :             {
    6915           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    6916             :                          "Invalid value in flag_values[%d] = %f", nIdxFlag,
    6917           0 :                          adfValues[nIdxFlag]);
    6918           0 :                 return false;
    6919             :             }
    6920             : 
    6921          10 :             if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
    6922             :             {
    6923           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    6924             :                          "Invalid value in flag_masks[%d] = %f", nIdxFlag,
    6925           0 :                          adfMasks[nIdxFlag]);
    6926           0 :                 return false;
    6927             :             }
    6928             : 
    6929          10 :             if (bHasFlagValues)
    6930             :             {
    6931          12 :                 m_anValidFlagValues.push_back(
    6932           6 :                     static_cast<uint32_t>(adfValues[nIdxFlag]));
    6933             :             }
    6934             : 
    6935          10 :             if (bHasFlagMasks)
    6936             :             {
    6937          12 :                 m_anValidFlagMasks.push_back(
    6938           6 :                     static_cast<uint32_t>(adfMasks[nIdxFlag]));
    6939             :             }
    6940             :         }
    6941             :     }
    6942             : 
    6943          42 :     return true;
    6944             : }
    6945             : 
    6946             : /************************************************************************/
    6947             : /*                             IRead()                                  */
    6948             : /************************************************************************/
    6949             : 
    6950          51 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6951             :                             const GInt64 *arrayStep,
    6952             :                             const GPtrDiff_t *bufferStride,
    6953             :                             const GDALExtendedDataType &bufferDataType,
    6954             :                             void *pDstBuffer) const
    6955             : {
    6956          51 :     size_t nElts = 1;
    6957          51 :     const size_t nDims = GetDimensionCount();
    6958         102 :     std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
    6959         139 :     for (size_t i = 0; i < nDims; i++)
    6960          88 :         nElts *= count[i];
    6961          51 :     if (nDims > 0)
    6962             :     {
    6963          46 :         tmpBufferStrideVector.back() = 1;
    6964          88 :         for (size_t i = nDims - 1; i > 0;)
    6965             :         {
    6966          42 :             --i;
    6967          42 :             tmpBufferStrideVector[i] =
    6968          42 :                 tmpBufferStrideVector[i + 1] * count[i + 1];
    6969             :         }
    6970             :     }
    6971             : 
    6972             :     /* Optimized case: if we are an integer data type and that there is no */
    6973             :     /* attribute that can be used to set mask = 0, then fill the mask buffer */
    6974             :     /* directly */
    6975          49 :     if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
    6976          74 :         !m_bHasValidMax && m_anValidFlagValues.empty() &&
    6977          34 :         m_anValidFlagMasks.empty() &&
    6978         111 :         m_poParent->GetRawNoDataValue() == nullptr &&
    6979          11 :         GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
    6980             :     {
    6981           7 :         const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
    6982           7 :         if (bBufferDataTypeIsByte)  // Byte case
    6983             :         {
    6984           4 :             bool bContiguous = true;
    6985          10 :             for (size_t i = 0; i < nDims; i++)
    6986             :             {
    6987           7 :                 if (bufferStride[i] != tmpBufferStrideVector[i])
    6988             :                 {
    6989           1 :                     bContiguous = false;
    6990           1 :                     break;
    6991             :                 }
    6992             :             }
    6993           4 :             if (bContiguous)
    6994             :             {
    6995             :                 // CPLDebug("GDAL", "GetMask(): contiguous case");
    6996           3 :                 memset(pDstBuffer, 1, nElts);
    6997           3 :                 return true;
    6998             :             }
    6999             :         }
    7000             : 
    7001             :         struct Stack
    7002             :         {
    7003             :             size_t nIters = 0;
    7004             :             GByte *dst_ptr = nullptr;
    7005             :             GPtrDiff_t dst_inc_offset = 0;
    7006             :         };
    7007             : 
    7008           4 :         std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
    7009           4 :         const size_t nBufferDTSize = bufferDataType.GetSize();
    7010          13 :         for (size_t i = 0; i < nDims; i++)
    7011             :         {
    7012           9 :             stack[i].dst_inc_offset =
    7013           9 :                 static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    7014             :         }
    7015           4 :         stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    7016             : 
    7017           4 :         size_t dimIdx = 0;
    7018           4 :         const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
    7019             :         GByte abyOne[16];  // 16 is sizeof GDT_CFloat64
    7020           4 :         CPLAssert(nBufferDTSize <= 16);
    7021           4 :         const GByte flag = 1;
    7022             :         // Coverity misses that m_dt is of type Byte
    7023             :         // coverity[overrun-buffer-val]
    7024           4 :         GDALExtendedDataType::CopyValue(&flag, m_dt, abyOne, bufferDataType);
    7025             : 
    7026          28 :     lbl_next_depth:
    7027          28 :         if (dimIdx == nDimsMinus1)
    7028             :         {
    7029          19 :             auto nIters = nDims > 0 ? count[dimIdx] : 1;
    7030          19 :             GByte *dst_ptr = stack[dimIdx].dst_ptr;
    7031             : 
    7032             :             while (true)
    7033             :             {
    7034             :                 // cppcheck-suppress knownConditionTrueFalse
    7035          73 :                 if (bBufferDataTypeIsByte)
    7036             :                 {
    7037          24 :                     *dst_ptr = flag;
    7038             :                 }
    7039             :                 else
    7040             :                 {
    7041          49 :                     memcpy(dst_ptr, abyOne, nBufferDTSize);
    7042             :                 }
    7043             : 
    7044          73 :                 if ((--nIters) == 0)
    7045          19 :                     break;
    7046          54 :                 dst_ptr += stack[dimIdx].dst_inc_offset;
    7047             :             }
    7048             :         }
    7049             :         else
    7050             :         {
    7051           9 :             stack[dimIdx].nIters = count[dimIdx];
    7052             :             while (true)
    7053             :             {
    7054          24 :                 dimIdx++;
    7055          24 :                 stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    7056          24 :                 goto lbl_next_depth;
    7057          24 :             lbl_return_to_caller:
    7058          24 :                 dimIdx--;
    7059          24 :                 if ((--stack[dimIdx].nIters) == 0)
    7060           9 :                     break;
    7061          15 :                 stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    7062             :             }
    7063             :         }
    7064          28 :         if (dimIdx > 0)
    7065          24 :             goto lbl_return_to_caller;
    7066             : 
    7067           4 :         return true;
    7068             :     }
    7069             : 
    7070             :     const auto oTmpBufferDT =
    7071          44 :         GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
    7072             :             ? GDALExtendedDataType::Create(GDT_Float64)
    7073          88 :             : m_poParent->GetDataType();
    7074          44 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
    7075          44 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
    7076          44 :     if (!pTempBuffer)
    7077           0 :         return false;
    7078          88 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
    7079          44 :                           tmpBufferStrideVector.data(), oTmpBufferDT,
    7080             :                           pTempBuffer))
    7081             :     {
    7082           0 :         VSIFree(pTempBuffer);
    7083           0 :         return false;
    7084             :     }
    7085             : 
    7086          44 :     switch (oTmpBufferDT.GetNumericDataType())
    7087             :     {
    7088           7 :         case GDT_Byte:
    7089           7 :             ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
    7090             :                                 pTempBuffer, oTmpBufferDT,
    7091             :                                 tmpBufferStrideVector);
    7092           7 :             break;
    7093             : 
    7094           0 :         case GDT_Int8:
    7095           0 :             ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
    7096             :                                 pTempBuffer, oTmpBufferDT,
    7097             :                                 tmpBufferStrideVector);
    7098           0 :             break;
    7099             : 
    7100           1 :         case GDT_UInt16:
    7101           1 :             ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
    7102             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
    7103             :                                   tmpBufferStrideVector);
    7104           1 :             break;
    7105             : 
    7106          14 :         case GDT_Int16:
    7107          14 :             ReadInternal<GInt16>(count, bufferStride, bufferDataType,
    7108             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7109             :                                  tmpBufferStrideVector);
    7110          14 :             break;
    7111             : 
    7112           1 :         case GDT_UInt32:
    7113           1 :             ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
    7114             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
    7115             :                                   tmpBufferStrideVector);
    7116           1 :             break;
    7117             : 
    7118           5 :         case GDT_Int32:
    7119           5 :             ReadInternal<GInt32>(count, bufferStride, bufferDataType,
    7120             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7121             :                                  tmpBufferStrideVector);
    7122           5 :             break;
    7123             : 
    7124           0 :         case GDT_UInt64:
    7125           0 :             ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
    7126             :                                         pDstBuffer, pTempBuffer, oTmpBufferDT,
    7127             :                                         tmpBufferStrideVector);
    7128           0 :             break;
    7129             : 
    7130           0 :         case GDT_Int64:
    7131           0 :             ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
    7132             :                                        pDstBuffer, pTempBuffer, oTmpBufferDT,
    7133             :                                        tmpBufferStrideVector);
    7134           0 :             break;
    7135             : 
    7136           0 :         case GDT_Float16:
    7137           0 :             ReadInternal<GFloat16>(count, bufferStride, bufferDataType,
    7138             :                                    pDstBuffer, pTempBuffer, oTmpBufferDT,
    7139             :                                    tmpBufferStrideVector);
    7140           0 :             break;
    7141             : 
    7142           7 :         case GDT_Float32:
    7143           7 :             ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
    7144             :                                 pTempBuffer, oTmpBufferDT,
    7145             :                                 tmpBufferStrideVector);
    7146           7 :             break;
    7147             : 
    7148           9 :         case GDT_Float64:
    7149           9 :             ReadInternal<double>(count, bufferStride, bufferDataType,
    7150             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7151             :                                  tmpBufferStrideVector);
    7152           9 :             break;
    7153           0 :         case GDT_Unknown:
    7154             :         case GDT_CInt16:
    7155             :         case GDT_CInt32:
    7156             :         case GDT_CFloat16:
    7157             :         case GDT_CFloat32:
    7158             :         case GDT_CFloat64:
    7159             :         case GDT_TypeCount:
    7160           0 :             CPLAssert(false);
    7161             :             break;
    7162             :     }
    7163             : 
    7164          44 :     VSIFree(pTempBuffer);
    7165             : 
    7166          44 :     return true;
    7167             : }
    7168             : 
    7169             : /************************************************************************/
    7170             : /*                          IsValidForDT()                              */
    7171             : /************************************************************************/
    7172             : 
    7173          40 : template <typename Type> static bool IsValidForDT(double dfVal)
    7174             : {
    7175          40 :     if (std::isnan(dfVal))
    7176           0 :         return false;
    7177          40 :     if (dfVal < static_cast<double>(cpl::NumericLimits<Type>::lowest()))
    7178           0 :         return false;
    7179          40 :     if (dfVal > static_cast<double>(cpl::NumericLimits<Type>::max()))
    7180           0 :         return false;
    7181          40 :     return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
    7182             : }
    7183             : 
    7184           9 : template <> bool IsValidForDT<double>(double)
    7185             : {
    7186           9 :     return true;
    7187             : }
    7188             : 
    7189             : /************************************************************************/
    7190             : /*                              IsNan()                                 */
    7191             : /************************************************************************/
    7192             : 
    7193        1438 : template <typename Type> inline bool IsNan(Type)
    7194             : {
    7195        1438 :     return false;
    7196             : }
    7197             : 
    7198          65 : template <> bool IsNan<double>(double val)
    7199             : {
    7200          65 :     return std::isnan(val);
    7201             : }
    7202             : 
    7203          26 : template <> bool IsNan<float>(float val)
    7204             : {
    7205          26 :     return std::isnan(val);
    7206             : }
    7207             : 
    7208             : /************************************************************************/
    7209             : /*                         ReadInternal()                               */
    7210             : /************************************************************************/
    7211             : 
    7212             : template <typename Type>
    7213          44 : void GDALMDArrayMask::ReadInternal(
    7214             :     const size_t *count, const GPtrDiff_t *bufferStride,
    7215             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    7216             :     const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
    7217             :     const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
    7218             : {
    7219          44 :     const size_t nDims = GetDimensionCount();
    7220             : 
    7221         220 :     const auto castValue = [](bool &bHasVal, double dfVal) -> Type
    7222             :     {
    7223         220 :         if (bHasVal)
    7224             :         {
    7225          49 :             if (IsValidForDT<Type>(dfVal))
    7226             :             {
    7227          49 :                 return static_cast<Type>(dfVal);
    7228             :             }
    7229             :             else
    7230             :             {
    7231           0 :                 bHasVal = false;
    7232             :             }
    7233             :         }
    7234         171 :         return 0;
    7235             :     };
    7236             : 
    7237          44 :     const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
    7238          44 :     bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
    7239             :     const Type nNoDataValue =
    7240          44 :         castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
    7241          44 :     bool bHasMissingValue = m_bHasMissingValue;
    7242          44 :     const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
    7243          44 :     bool bHasFillValue = m_bHasFillValue;
    7244          44 :     const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
    7245          44 :     bool bHasValidMin = m_bHasValidMin;
    7246          44 :     const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
    7247          44 :     bool bHasValidMax = m_bHasValidMax;
    7248          44 :     const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
    7249          44 :     const bool bHasValidFlags =
    7250          44 :         !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
    7251             : 
    7252         351 :     const auto IsValidFlag = [this](Type v)
    7253             :     {
    7254          54 :         if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
    7255             :         {
    7256          20 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
    7257             :             {
    7258          12 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
    7259             :                     m_anValidFlagValues[i])
    7260             :                 {
    7261           4 :                     return true;
    7262             :                 }
    7263             :             }
    7264             :         }
    7265          42 :         else if (!m_anValidFlagValues.empty())
    7266             :         {
    7267          49 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
    7268             :             {
    7269          29 :                 if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
    7270             :                 {
    7271           4 :                     return true;
    7272             :                 }
    7273             :             }
    7274             :         }
    7275             :         else /* if( !m_anValidFlagMasks.empty() ) */
    7276             :         {
    7277          31 :             for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
    7278             :             {
    7279          22 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
    7280             :                 {
    7281           9 :                     return true;
    7282             :                 }
    7283             :             }
    7284             :         }
    7285          37 :         return false;
    7286             :     };
    7287             : 
    7288             : #define GET_MASK_FOR_SAMPLE(v)                                                 \
    7289             :     static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
    7290             :                        !(bHasMissingValue && v == nMissingValue) &&            \
    7291             :                        !(bHasFillValue && v == nFillValue) &&                  \
    7292             :                        !(bHasValidMin && v < nValidMin) &&                     \
    7293             :                        !(bHasValidMax && v > nValidMax) &&                     \
    7294             :                        (!bHasValidFlags || IsValidFlag(v)));
    7295             : 
    7296          44 :     const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
    7297             :     /* Optimized case: Byte output and output buffer is contiguous */
    7298          44 :     if (bBufferDataTypeIsByte)
    7299             :     {
    7300          40 :         bool bContiguous = true;
    7301         103 :         for (size_t i = 0; i < nDims; i++)
    7302             :         {
    7303          64 :             if (bufferStride[i] != tmpBufferStrideVector[i])
    7304             :             {
    7305           1 :                 bContiguous = false;
    7306           1 :                 break;
    7307             :             }
    7308             :         }
    7309          40 :         if (bContiguous)
    7310             :         {
    7311          39 :             size_t nElts = 1;
    7312         102 :             for (size_t i = 0; i < nDims; i++)
    7313          63 :                 nElts *= count[i];
    7314             : 
    7315        1113 :             for (size_t i = 0; i < nElts; i++)
    7316             :             {
    7317        1074 :                 const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
    7318        1074 :                 static_cast<GByte *>(pDstBuffer)[i] =
    7319        1074 :                     GET_MASK_FOR_SAMPLE(*pSrc);
    7320             :             }
    7321          39 :             return;
    7322             :         }
    7323             :     }
    7324             : 
    7325           5 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
    7326             : 
    7327             :     struct Stack
    7328             :     {
    7329             :         size_t nIters = 0;
    7330             :         const GByte *src_ptr = nullptr;
    7331             :         GByte *dst_ptr = nullptr;
    7332             :         GPtrDiff_t src_inc_offset = 0;
    7333             :         GPtrDiff_t dst_inc_offset = 0;
    7334             :     };
    7335             : 
    7336          10 :     std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
    7337           5 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    7338          15 :     for (size_t i = 0; i < nDims; i++)
    7339             :     {
    7340          20 :         stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
    7341          10 :             tmpBufferStrideVector[i] * nTmpBufferDTSize);
    7342          10 :         stack[i].dst_inc_offset =
    7343          10 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    7344             :     }
    7345           5 :     stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
    7346           5 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    7347             : 
    7348           5 :     size_t dimIdx = 0;
    7349           5 :     const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
    7350             :     GByte abyZeroOrOne[2][16];  // 16 is sizeof GDT_CFloat64
    7351           5 :     CPLAssert(nBufferDTSize <= 16);
    7352          15 :     for (GByte flag = 0; flag <= 1; flag++)
    7353             :     {
    7354             :         // Coverity misses that m_dt is of type Byte
    7355             :         // coverity[overrun-buffer-val]
    7356          10 :         GDALExtendedDataType::CopyValue(&flag, m_dt, abyZeroOrOne[flag],
    7357             :                                         bufferDataType);
    7358             :     }
    7359             : 
    7360          43 : lbl_next_depth:
    7361          43 :     if (dimIdx == nDimsMinus1)
    7362             :     {
    7363          35 :         auto nIters = nDims > 0 ? count[dimIdx] : 1;
    7364          35 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
    7365          35 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
    7366             : 
    7367         420 :         while (true)
    7368             :         {
    7369         455 :             const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
    7370         455 :             const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
    7371             : 
    7372         455 :             if (bBufferDataTypeIsByte)
    7373             :             {
    7374          24 :                 *dst_ptr = flag;
    7375             :             }
    7376             :             else
    7377             :             {
    7378         431 :                 memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
    7379             :             }
    7380             : 
    7381         455 :             if ((--nIters) == 0)
    7382          35 :                 break;
    7383         420 :             src_ptr += stack[dimIdx].src_inc_offset;
    7384         420 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    7385             :         }
    7386             :     }
    7387             :     else
    7388             :     {
    7389           8 :         stack[dimIdx].nIters = count[dimIdx];
    7390             :         while (true)
    7391             :         {
    7392          38 :             dimIdx++;
    7393          38 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    7394          38 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    7395          38 :             goto lbl_next_depth;
    7396          38 :         lbl_return_to_caller:
    7397          38 :             dimIdx--;
    7398          38 :             if ((--stack[dimIdx].nIters) == 0)
    7399           8 :                 break;
    7400          30 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    7401          30 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    7402             :         }
    7403             :     }
    7404          43 :     if (dimIdx > 0)
    7405          38 :         goto lbl_return_to_caller;
    7406             : }
    7407             : 
    7408             : /************************************************************************/
    7409             : /*                            GetMask()                                 */
    7410             : /************************************************************************/
    7411             : 
    7412             : /** Return an array that is a mask for the current array
    7413             : 
    7414             :  This array will be of type Byte, with values set to 0 to indicate invalid
    7415             :  pixels of the current array, and values set to 1 to indicate valid pixels.
    7416             : 
    7417             :  The generic implementation honours the NoDataValue, as well as various
    7418             :  netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
    7419             :  and valid_range.
    7420             : 
    7421             :  Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
    7422             :  can be used to specify strings of the "flag_meanings" attribute
    7423             :  (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
    7424             :  for which pixels matching any of those flags will be set at 1 in the mask array,
    7425             :  and pixels matching none of those flags will be set at 0.
    7426             :  For example, let's consider the following netCDF variable defined with:
    7427             :  \verbatim
    7428             :  l2p_flags:valid_min = 0s ;
    7429             :  l2p_flags:valid_max = 256s ;
    7430             :  l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
    7431             :  l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
    7432             :  \endverbatim
    7433             : 
    7434             :  GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
    7435             :  - for pixel values *outside* valid_range [0,256], the mask value will be 0.
    7436             :  - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
    7437             :    will be 1.
    7438             :  - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
    7439             :    will be 0.
    7440             : 
    7441             :  This is the same as the C function GDALMDArrayGetMask().
    7442             : 
    7443             :  @param papszOptions NULL-terminated list of options, or NULL.
    7444             : 
    7445             :  @return a new array, that holds a reference to the original one, and thus is
    7446             :  a view of it (not a copy), or nullptr in case of error.
    7447             : */
    7448             : std::shared_ptr<GDALMDArray>
    7449          49 : GDALMDArray::GetMask(CSLConstList papszOptions) const
    7450             : {
    7451          98 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    7452          49 :     if (!self)
    7453             :     {
    7454           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    7455             :                  "Driver implementation issue: m_pSelf not set !");
    7456           0 :         return nullptr;
    7457             :     }
    7458          49 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    7459             :     {
    7460           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    7461             :                  "GetMask() only supports numeric data type");
    7462           1 :         return nullptr;
    7463             :     }
    7464          48 :     return GDALMDArrayMask::Create(self, papszOptions);
    7465             : }
    7466             : 
    7467             : /************************************************************************/
    7468             : /*                         IsRegularlySpaced()                          */
    7469             : /************************************************************************/
    7470             : 
    7471             : /** Returns whether an array is a 1D regularly spaced array.
    7472             :  *
    7473             :  * @param[out] dfStart     First value in the array
    7474             :  * @param[out] dfIncrement Increment/spacing between consecutive values.
    7475             :  * @return true if the array is regularly spaced.
    7476             :  */
    7477         189 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
    7478             : {
    7479         189 :     dfStart = 0;
    7480         189 :     dfIncrement = 0;
    7481         189 :     if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
    7482           0 :         return false;
    7483         189 :     const auto nSize = GetDimensions()[0]->GetSize();
    7484         189 :     if (nSize <= 1 || nSize > 10 * 1000 * 1000)
    7485           2 :         return false;
    7486             : 
    7487         187 :     size_t nCount = static_cast<size_t>(nSize);
    7488         374 :     std::vector<double> adfTmp;
    7489             :     try
    7490             :     {
    7491         187 :         adfTmp.resize(nCount);
    7492             :     }
    7493           0 :     catch (const std::exception &)
    7494             :     {
    7495           0 :         return false;
    7496             :     }
    7497             : 
    7498         187 :     GUInt64 anStart[1] = {0};
    7499         187 :     size_t anCount[1] = {nCount};
    7500             : 
    7501             :     const auto IsRegularlySpacedInternal =
    7502       83950 :         [&dfStart, &dfIncrement, &anCount, &adfTmp]()
    7503             :     {
    7504         261 :         dfStart = adfTmp[0];
    7505         261 :         dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
    7506         261 :         if (dfIncrement == 0)
    7507             :         {
    7508           3 :             return false;
    7509             :         }
    7510       20920 :         for (size_t i = 1; i < anCount[0]; i++)
    7511             :         {
    7512       20662 :             if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
    7513       20662 :                 1e-3 * fabs(dfIncrement))
    7514             :             {
    7515           0 :                 return false;
    7516             :             }
    7517             :         }
    7518         258 :         return true;
    7519         187 :     };
    7520             : 
    7521             :     // First try with the first block(s). This can avoid excessive processing
    7522             :     // time, for example with Zarr datasets.
    7523             :     // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
    7524             :     // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
    7525         187 :     const auto nBlockSize = GetBlockSize()[0];
    7526         187 :     if (nCount >= 5 && nBlockSize <= nCount / 2)
    7527             :     {
    7528             :         size_t nReducedCount =
    7529          77 :             std::max<size_t>(3, static_cast<size_t>(nBlockSize));
    7530         440 :         while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
    7531         363 :             nReducedCount *= 2;
    7532          77 :         anCount[0] = nReducedCount;
    7533          77 :         if (!Read(anStart, anCount, nullptr, nullptr,
    7534         154 :                   GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
    7535             :         {
    7536           0 :             return false;
    7537             :         }
    7538          77 :         if (!IsRegularlySpacedInternal())
    7539             :         {
    7540           3 :             return false;
    7541             :         }
    7542             : 
    7543             :         // Get next values
    7544          74 :         anStart[0] = nReducedCount;
    7545          74 :         anCount[0] = nCount - nReducedCount;
    7546             :     }
    7547             : 
    7548         184 :     if (!Read(anStart, anCount, nullptr, nullptr,
    7549         368 :               GDALExtendedDataType::Create(GDT_Float64),
    7550         184 :               &adfTmp[static_cast<size_t>(anStart[0])]))
    7551             :     {
    7552           0 :         return false;
    7553             :     }
    7554             : 
    7555         184 :     return IsRegularlySpacedInternal();
    7556             : }
    7557             : 
    7558             : /************************************************************************/
    7559             : /*                         GuessGeoTransform()                          */
    7560             : /************************************************************************/
    7561             : 
    7562             : /** Returns whether 2 specified dimensions form a geotransform
    7563             :  *
    7564             :  * @param nDimX                Index of the X axis.
    7565             :  * @param nDimY                Index of the Y axis.
    7566             :  * @param bPixelIsPoint        Whether the geotransform should be returned
    7567             :  *                             with the pixel-is-point (pixel-center) convention
    7568             :  *                             (bPixelIsPoint = true), or with the pixel-is-area
    7569             :  *                             (top left corner convention)
    7570             :  *                             (bPixelIsPoint = false)
    7571             :  * @param[out] adfGeoTransform Computed geotransform
    7572             :  * @return true if a geotransform could be computed.
    7573             :  */
    7574         227 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
    7575             :                                     bool bPixelIsPoint,
    7576             :                                     double adfGeoTransform[6]) const
    7577             : {
    7578         227 :     const auto &dims(GetDimensions());
    7579         454 :     auto poVarX = dims[nDimX]->GetIndexingVariable();
    7580         454 :     auto poVarY = dims[nDimY]->GetIndexingVariable();
    7581         227 :     double dfXStart = 0.0;
    7582         227 :     double dfXSpacing = 0.0;
    7583         227 :     double dfYStart = 0.0;
    7584         227 :     double dfYSpacing = 0.0;
    7585         511 :     if (poVarX && poVarX->GetDimensionCount() == 1 &&
    7586         284 :         poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
    7587         332 :         poVarY && poVarY->GetDimensionCount() == 1 &&
    7588          95 :         poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
    7589         459 :         poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
    7590          90 :         poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
    7591             :     {
    7592          90 :         adfGeoTransform[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
    7593          90 :         adfGeoTransform[1] = dfXSpacing;
    7594          90 :         adfGeoTransform[2] = 0;
    7595          90 :         adfGeoTransform[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
    7596          90 :         adfGeoTransform[4] = 0;
    7597          90 :         adfGeoTransform[5] = dfYSpacing;
    7598          90 :         return true;
    7599             :     }
    7600         137 :     return false;
    7601             : }
    7602             : 
    7603             : /************************************************************************/
    7604             : /*                       GDALMDArrayResampled                           */
    7605             : /************************************************************************/
    7606             : 
    7607             : class GDALMDArrayResampledDataset;
    7608             : 
    7609             : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
    7610             : {
    7611             :   protected:
    7612             :     CPLErr IReadBlock(int, int, void *) override;
    7613             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
    7614             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
    7615             :                      GDALDataType eBufType, GSpacing nPixelSpaceBuf,
    7616             :                      GSpacing nLineSpaceBuf,
    7617             :                      GDALRasterIOExtraArg *psExtraArg) override;
    7618             : 
    7619             :   public:
    7620             :     explicit GDALMDArrayResampledDatasetRasterBand(
    7621             :         GDALMDArrayResampledDataset *poDSIn);
    7622             : 
    7623             :     double GetNoDataValue(int *pbHasNoData) override;
    7624             : };
    7625             : 
    7626             : class GDALMDArrayResampledDataset final : public GDALPamDataset
    7627             : {
    7628             :     friend class GDALMDArrayResampled;
    7629             :     friend class GDALMDArrayResampledDatasetRasterBand;
    7630             : 
    7631             :     std::shared_ptr<GDALMDArray> m_poArray;
    7632             :     const size_t m_iXDim;
    7633             :     const size_t m_iYDim;
    7634             :     double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
    7635             :     bool m_bHasGT = false;
    7636             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
    7637             : 
    7638             :     std::vector<GUInt64> m_anOffset{};
    7639             :     std::vector<size_t> m_anCount{};
    7640             :     std::vector<GPtrDiff_t> m_anStride{};
    7641             : 
    7642             :     std::string m_osFilenameLong{};
    7643             :     std::string m_osFilenameLat{};
    7644             : 
    7645             :   public:
    7646          24 :     GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
    7647             :                                 size_t iXDim, size_t iYDim)
    7648          24 :         : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
    7649          24 :           m_anOffset(m_poArray->GetDimensionCount(), 0),
    7650          24 :           m_anCount(m_poArray->GetDimensionCount(), 1),
    7651          72 :           m_anStride(m_poArray->GetDimensionCount(), 0)
    7652             :     {
    7653          24 :         const auto &dims(m_poArray->GetDimensions());
    7654             : 
    7655          24 :         nRasterYSize = static_cast<int>(
    7656          24 :             std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
    7657          24 :         nRasterXSize = static_cast<int>(
    7658          24 :             std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
    7659             : 
    7660          24 :         m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false,
    7661          24 :                                                 m_adfGeoTransform);
    7662             : 
    7663          24 :         SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
    7664          24 :     }
    7665             : 
    7666          48 :     ~GDALMDArrayResampledDataset()
    7667          24 :     {
    7668          24 :         if (!m_osFilenameLong.empty())
    7669           5 :             VSIUnlink(m_osFilenameLong.c_str());
    7670          24 :         if (!m_osFilenameLat.empty())
    7671           5 :             VSIUnlink(m_osFilenameLat.c_str());
    7672          48 :     }
    7673             : 
    7674          43 :     CPLErr GetGeoTransform(double *padfGeoTransform) override
    7675             :     {
    7676          43 :         memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
    7677          43 :         return m_bHasGT ? CE_None : CE_Failure;
    7678             :     }
    7679             : 
    7680         105 :     const OGRSpatialReference *GetSpatialRef() const override
    7681             :     {
    7682         105 :         m_poSRS = m_poArray->GetSpatialRef();
    7683         105 :         if (m_poSRS)
    7684             :         {
    7685          79 :             m_poSRS.reset(m_poSRS->Clone());
    7686         158 :             auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
    7687         237 :             for (auto &m : axisMapping)
    7688             :             {
    7689         158 :                 if (m == static_cast<int>(m_iXDim) + 1)
    7690          79 :                     m = 1;
    7691          79 :                 else if (m == static_cast<int>(m_iYDim) + 1)
    7692          79 :                     m = 2;
    7693             :             }
    7694          79 :             m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
    7695             :         }
    7696         105 :         return m_poSRS.get();
    7697             :     }
    7698             : 
    7699           5 :     void SetGeolocationArray(const std::string &osFilenameLong,
    7700             :                              const std::string &osFilenameLat)
    7701             :     {
    7702           5 :         m_osFilenameLong = osFilenameLong;
    7703           5 :         m_osFilenameLat = osFilenameLat;
    7704          10 :         CPLStringList aosGeoLoc;
    7705           5 :         aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
    7706           5 :         aosGeoLoc.SetNameValue("LINE_STEP", "1");
    7707           5 :         aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
    7708           5 :         aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
    7709           5 :         aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG);  // FIXME?
    7710           5 :         aosGeoLoc.SetNameValue("X_BAND", "1");
    7711           5 :         aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
    7712           5 :         aosGeoLoc.SetNameValue("Y_BAND", "1");
    7713           5 :         aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
    7714           5 :         aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
    7715           5 :         SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
    7716           5 :     }
    7717             : };
    7718             : 
    7719             : /************************************************************************/
    7720             : /*                   GDALMDArrayResampledDatasetRasterBand()            */
    7721             : /************************************************************************/
    7722             : 
    7723          24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
    7724          24 :     GDALMDArrayResampledDataset *poDSIn)
    7725             : {
    7726          24 :     const auto &poArray(poDSIn->m_poArray);
    7727          24 :     const auto blockSize(poArray->GetBlockSize());
    7728          24 :     nBlockYSize = (blockSize[poDSIn->m_iYDim])
    7729          24 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    7730          13 :                                                   blockSize[poDSIn->m_iYDim]))
    7731          24 :                       : 1;
    7732          24 :     nBlockXSize = blockSize[poDSIn->m_iXDim]
    7733          13 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    7734          13 :                                                   blockSize[poDSIn->m_iXDim]))
    7735          24 :                       : poDSIn->GetRasterXSize();
    7736          24 :     eDataType = poArray->GetDataType().GetNumericDataType();
    7737          24 :     eAccess = poDSIn->eAccess;
    7738          24 : }
    7739             : 
    7740             : /************************************************************************/
    7741             : /*                           GetNoDataValue()                           */
    7742             : /************************************************************************/
    7743             : 
    7744          50 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
    7745             : {
    7746          50 :     auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
    7747          50 :     const auto &poArray(l_poDS->m_poArray);
    7748          50 :     bool bHasNodata = false;
    7749          50 :     double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
    7750          50 :     if (pbHasNoData)
    7751          46 :         *pbHasNoData = bHasNodata;
    7752          50 :     return dfRes;
    7753             : }
    7754             : 
    7755             : /************************************************************************/
    7756             : /*                            IReadBlock()                              */
    7757             : /************************************************************************/
    7758             : 
    7759           0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
    7760             :                                                          int nBlockYOff,
    7761             :                                                          void *pImage)
    7762             : {
    7763           0 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    7764           0 :     const int nXOff = nBlockXOff * nBlockXSize;
    7765           0 :     const int nYOff = nBlockYOff * nBlockYSize;
    7766           0 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    7767           0 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    7768             :     GDALRasterIOExtraArg sExtraArg;
    7769           0 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    7770           0 :     return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    7771             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    7772           0 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    7773             : }
    7774             : 
    7775             : /************************************************************************/
    7776             : /*                            IRasterIO()                               */
    7777             : /************************************************************************/
    7778             : 
    7779          32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
    7780             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
    7781             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
    7782             :     GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
    7783             :     GDALRasterIOExtraArg *psExtraArg)
    7784             : {
    7785          32 :     auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
    7786          32 :     const auto &poArray(l_poDS->m_poArray);
    7787          32 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
    7788          32 :     if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
    7789          32 :         nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
    7790          32 :         (nLineSpaceBuf % nBufferDTSize) == 0)
    7791             :     {
    7792          32 :         l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
    7793          32 :         l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
    7794          64 :         l_poDS->m_anStride[l_poDS->m_iXDim] =
    7795          32 :             static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
    7796             : 
    7797          32 :         l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
    7798          32 :         l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
    7799          64 :         l_poDS->m_anStride[l_poDS->m_iYDim] =
    7800          32 :             static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
    7801             : 
    7802          64 :         return poArray->Read(l_poDS->m_anOffset.data(),
    7803          32 :                              l_poDS->m_anCount.data(), nullptr,
    7804          32 :                              l_poDS->m_anStride.data(),
    7805          64 :                              GDALExtendedDataType::Create(eBufType), pData)
    7806          32 :                    ? CE_None
    7807          32 :                    : CE_Failure;
    7808             :     }
    7809           0 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    7810             :                                      pData, nBufXSize, nBufYSize, eBufType,
    7811           0 :                                      nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
    7812             : }
    7813             : 
    7814             : class GDALMDArrayResampled final : public GDALPamMDArray
    7815             : {
    7816             :   private:
    7817             :     std::shared_ptr<GDALMDArray> m_poParent{};
    7818             :     std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
    7819             :     std::vector<GUInt64> m_anBlockSize;
    7820             :     GDALExtendedDataType m_dt;
    7821             :     std::shared_ptr<OGRSpatialReference> m_poSRS{};
    7822             :     std::shared_ptr<GDALMDArray> m_poVarX{};
    7823             :     std::shared_ptr<GDALMDArray> m_poVarY{};
    7824             :     std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
    7825             :     std::unique_ptr<GDALDataset> m_poReprojectedDS{};
    7826             : 
    7827             :   protected:
    7828          21 :     GDALMDArrayResampled(
    7829             :         const std::shared_ptr<GDALMDArray> &poParent,
    7830             :         const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
    7831             :         const std::vector<GUInt64> &anBlockSize)
    7832          42 :         : GDALAbstractMDArray(std::string(),
    7833          42 :                               "Resampled view of " + poParent->GetFullName()),
    7834             :           GDALPamMDArray(
    7835          42 :               std::string(), "Resampled view of " + poParent->GetFullName(),
    7836          42 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
    7837          21 :           m_poParent(std::move(poParent)), m_apoDims(apoDims),
    7838         105 :           m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
    7839             :     {
    7840          21 :         CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
    7841          21 :         CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
    7842          21 :     }
    7843             : 
    7844             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    7845             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    7846             :                const GDALExtendedDataType &bufferDataType,
    7847             :                void *pDstBuffer) const override;
    7848             : 
    7849             :   public:
    7850             :     static std::shared_ptr<GDALMDArray>
    7851             :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    7852             :            const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
    7853             :            GDALRIOResampleAlg resampleAlg,
    7854             :            const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
    7855             : 
    7856          42 :     ~GDALMDArrayResampled()
    7857          21 :     {
    7858             :         // First close the warped VRT
    7859          21 :         m_poReprojectedDS.reset();
    7860          21 :         m_poParentDS.reset();
    7861          42 :     }
    7862             : 
    7863          11 :     bool IsWritable() const override
    7864             :     {
    7865          11 :         return false;
    7866             :     }
    7867             : 
    7868          74 :     const std::string &GetFilename() const override
    7869             :     {
    7870          74 :         return m_poParent->GetFilename();
    7871             :     }
    7872             : 
    7873             :     const std::vector<std::shared_ptr<GDALDimension>> &
    7874         257 :     GetDimensions() const override
    7875             :     {
    7876         257 :         return m_apoDims;
    7877             :     }
    7878             : 
    7879         109 :     const GDALExtendedDataType &GetDataType() const override
    7880             :     {
    7881         109 :         return m_dt;
    7882             :     }
    7883             : 
    7884          21 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    7885             :     {
    7886          21 :         return m_poSRS;
    7887             :     }
    7888             : 
    7889          12 :     std::vector<GUInt64> GetBlockSize() const override
    7890             :     {
    7891          12 :         return m_anBlockSize;
    7892             :     }
    7893             : 
    7894             :     std::shared_ptr<GDALAttribute>
    7895           1 :     GetAttribute(const std::string &osName) const override
    7896             :     {
    7897           1 :         return m_poParent->GetAttribute(osName);
    7898             :     }
    7899             : 
    7900             :     std::vector<std::shared_ptr<GDALAttribute>>
    7901          12 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    7902             :     {
    7903          12 :         return m_poParent->GetAttributes(papszOptions);
    7904             :     }
    7905             : 
    7906           1 :     const std::string &GetUnit() const override
    7907             :     {
    7908           1 :         return m_poParent->GetUnit();
    7909             :     }
    7910             : 
    7911           1 :     const void *GetRawNoDataValue() const override
    7912             :     {
    7913           1 :         return m_poParent->GetRawNoDataValue();
    7914             :     }
    7915             : 
    7916           1 :     double GetOffset(bool *pbHasOffset,
    7917             :                      GDALDataType *peStorageType) const override
    7918             :     {
    7919           1 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    7920             :     }
    7921             : 
    7922           1 :     double GetScale(bool *pbHasScale,
    7923             :                     GDALDataType *peStorageType) const override
    7924             :     {
    7925           1 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    7926             :     }
    7927             : };
    7928             : 
    7929             : /************************************************************************/
    7930             : /*                   GDALMDArrayResampled::Create()                     */
    7931             : /************************************************************************/
    7932             : 
    7933          29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
    7934             :     const std::shared_ptr<GDALMDArray> &poParent,
    7935             :     const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
    7936             :     GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
    7937             :     CSLConstList /* papszOptions */)
    7938             : {
    7939          29 :     const char *pszResampleAlg = "nearest";
    7940          29 :     bool unsupported = false;
    7941          29 :     switch (resampleAlg)
    7942             :     {
    7943          16 :         case GRIORA_NearestNeighbour:
    7944          16 :             pszResampleAlg = "nearest";
    7945          16 :             break;
    7946           2 :         case GRIORA_Bilinear:
    7947           2 :             pszResampleAlg = "bilinear";
    7948           2 :             break;
    7949           5 :         case GRIORA_Cubic:
    7950           5 :             pszResampleAlg = "cubic";
    7951           5 :             break;
    7952           1 :         case GRIORA_CubicSpline:
    7953           1 :             pszResampleAlg = "cubicspline";
    7954           1 :             break;
    7955           1 :         case GRIORA_Lanczos:
    7956           1 :             pszResampleAlg = "lanczos";
    7957           1 :             break;
    7958           1 :         case GRIORA_Average:
    7959           1 :             pszResampleAlg = "average";
    7960           1 :             break;
    7961           1 :         case GRIORA_Mode:
    7962           1 :             pszResampleAlg = "mode";
    7963           1 :             break;
    7964           1 :         case GRIORA_Gauss:
    7965           1 :             unsupported = true;
    7966           1 :             break;
    7967           0 :         case GRIORA_RESERVED_START:
    7968           0 :             unsupported = true;
    7969           0 :             break;
    7970           0 :         case GRIORA_RESERVED_END:
    7971           0 :             unsupported = true;
    7972           0 :             break;
    7973           1 :         case GRIORA_RMS:
    7974           1 :             pszResampleAlg = "rms";
    7975           1 :             break;
    7976             :     }
    7977          29 :     if (unsupported)
    7978             :     {
    7979           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    7980             :                  "Unsupported resample method for GetResampled()");
    7981           1 :         return nullptr;
    7982             :     }
    7983             : 
    7984          28 :     if (poParent->GetDimensionCount() < 2)
    7985             :     {
    7986           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    7987             :                  "GetResampled() only supports 2 dimensions or more");
    7988           1 :         return nullptr;
    7989             :     }
    7990             : 
    7991          27 :     const auto &aoParentDims = poParent->GetDimensions();
    7992          27 :     if (apoNewDimsIn.size() != aoParentDims.size())
    7993             :     {
    7994           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    7995             :                  "GetResampled(): apoNewDims size should be the same as "
    7996             :                  "GetDimensionCount()");
    7997           2 :         return nullptr;
    7998             :     }
    7999             : 
    8000          50 :     std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
    8001          25 :     apoNewDims.reserve(apoNewDimsIn.size());
    8002             : 
    8003          50 :     std::vector<GUInt64> anBlockSize;
    8004          25 :     anBlockSize.reserve(apoNewDimsIn.size());
    8005          50 :     const auto &anParentBlockSize = poParent->GetBlockSize();
    8006             : 
    8007          50 :     auto apoParentDims = poParent->GetDimensions();
    8008             :     // Special case for NASA EMIT datasets
    8009          30 :     const bool bYXBandOrder = (apoParentDims.size() == 3 &&
    8010           7 :                                apoParentDims[0]->GetName() == "downtrack" &&
    8011          32 :                                apoParentDims[1]->GetName() == "crosstrack" &&
    8012           2 :                                apoParentDims[2]->GetName() == "bands");
    8013             : 
    8014             :     const size_t iYDimParent =
    8015          25 :         bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
    8016             :     const size_t iXDimParent =
    8017          25 :         bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
    8018             : 
    8019          77 :     for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
    8020             :     {
    8021          53 :         if (i == iYDimParent || i == iXDimParent)
    8022          48 :             continue;
    8023           5 :         if (apoNewDimsIn[i] == nullptr)
    8024             :         {
    8025           3 :             apoNewDims.emplace_back(aoParentDims[i]);
    8026             :         }
    8027           3 :         else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
    8028           1 :                  apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
    8029             :         {
    8030           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    8031             :                      "GetResampled(): apoNewDims[%u] should be the same "
    8032             :                      "as its parent",
    8033             :                      i);
    8034           1 :             return nullptr;
    8035             :         }
    8036             :         else
    8037             :         {
    8038           1 :             apoNewDims.emplace_back(aoParentDims[i]);
    8039             :         }
    8040           4 :         anBlockSize.emplace_back(anParentBlockSize[i]);
    8041             :     }
    8042             : 
    8043             :     std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
    8044          48 :         new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
    8045             : 
    8046          24 :     double dfXStart = 0.0;
    8047          24 :     double dfXSpacing = 0.0;
    8048          24 :     bool gotXSpacing = false;
    8049          48 :     auto poNewDimX = apoNewDimsIn[iXDimParent];
    8050          24 :     if (poNewDimX)
    8051             :     {
    8052           4 :         if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
    8053             :         {
    8054           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    8055             :                      "Too big size for X dimension");
    8056           0 :             return nullptr;
    8057             :         }
    8058           4 :         auto var = poNewDimX->GetIndexingVariable();
    8059           4 :         if (var)
    8060             :         {
    8061           2 :             if (var->GetDimensionCount() != 1 ||
    8062           2 :                 var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
    8063           1 :                 !var->IsRegularlySpaced(dfXStart, dfXSpacing))
    8064             :             {
    8065           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    8066             :                          "New X dimension should be indexed by a regularly "
    8067             :                          "spaced variable");
    8068           0 :                 return nullptr;
    8069             :             }
    8070           1 :             gotXSpacing = true;
    8071             :         }
    8072             :     }
    8073             : 
    8074          24 :     double dfYStart = 0.0;
    8075          24 :     double dfYSpacing = 0.0;
    8076          48 :     auto poNewDimY = apoNewDimsIn[iYDimParent];
    8077          24 :     bool gotYSpacing = false;
    8078          24 :     if (poNewDimY)
    8079             :     {
    8080           4 :         if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
    8081             :         {
    8082           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    8083             :                      "Too big size for Y dimension");
    8084           0 :             return nullptr;
    8085             :         }
    8086           4 :         auto var = poNewDimY->GetIndexingVariable();
    8087           4 :         if (var)
    8088             :         {
    8089           2 :             if (var->GetDimensionCount() != 1 ||
    8090           2 :                 var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
    8091           1 :                 !var->IsRegularlySpaced(dfYStart, dfYSpacing))
    8092             :             {
    8093           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    8094             :                          "New Y dimension should be indexed by a regularly "
    8095             :                          "spaced variable");
    8096           0 :                 return nullptr;
    8097             :             }
    8098           1 :             gotYSpacing = true;
    8099             :         }
    8100             :     }
    8101             : 
    8102             :     // This limitation could probably be removed
    8103          24 :     if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
    8104             :     {
    8105           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    8106             :                  "Either none of new X or Y dimension should have an indexing "
    8107             :                  "variable, or both should both should have one.");
    8108           0 :         return nullptr;
    8109             :     }
    8110             : 
    8111          48 :     std::string osDstWKT;
    8112          24 :     if (poTargetSRS)
    8113             :     {
    8114           2 :         char *pszDstWKT = nullptr;
    8115           2 :         if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
    8116             :         {
    8117           0 :             CPLFree(pszDstWKT);
    8118           0 :             return nullptr;
    8119             :         }
    8120           2 :         osDstWKT = pszDstWKT;
    8121           2 :         CPLFree(pszDstWKT);
    8122             :     }
    8123             : 
    8124             :     // Use coordinate variables for geolocation array
    8125          48 :     const auto apoCoordinateVars = poParent->GetCoordinateVariables();
    8126          24 :     bool useGeolocationArray = false;
    8127          24 :     if (apoCoordinateVars.size() >= 2)
    8128             :     {
    8129           0 :         std::shared_ptr<GDALMDArray> poLongVar;
    8130           0 :         std::shared_ptr<GDALMDArray> poLatVar;
    8131          15 :         for (const auto &poCoordVar : apoCoordinateVars)
    8132             :         {
    8133          10 :             const auto &osName = poCoordVar->GetName();
    8134          30 :             const auto poAttr = poCoordVar->GetAttribute("standard_name");
    8135          20 :             std::string osStandardName;
    8136          12 :             if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
    8137           2 :                 poAttr->GetDimensionCount() == 0)
    8138             :             {
    8139           2 :                 const char *pszStandardName = poAttr->ReadAsString();
    8140           2 :                 if (pszStandardName)
    8141           2 :                     osStandardName = pszStandardName;
    8142             :             }
    8143          21 :             if (osName == "lon" || osName == "longitude" ||
    8144          21 :                 osName == "Longitude" || osStandardName == "longitude")
    8145             :             {
    8146           5 :                 poLongVar = poCoordVar;
    8147             :             }
    8148           6 :             else if (osName == "lat" || osName == "latitude" ||
    8149           6 :                      osName == "Latitude" || osStandardName == "latitude")
    8150             :             {
    8151           5 :                 poLatVar = poCoordVar;
    8152             :             }
    8153             :         }
    8154           5 :         if (poLatVar != nullptr && poLongVar != nullptr)
    8155             :         {
    8156           5 :             const auto longDimCount = poLongVar->GetDimensionCount();
    8157           5 :             const auto &longDims = poLongVar->GetDimensions();
    8158           5 :             const auto latDimCount = poLatVar->GetDimensionCount();
    8159           5 :             const auto &latDims = poLatVar->GetDimensions();
    8160           5 :             const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
    8161           5 :             const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
    8162           0 :             if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
    8163           5 :                 latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
    8164             :             {
    8165             :                 // Geolocation arrays are 1D, and of consistent size with
    8166             :                 // the variable
    8167           0 :                 useGeolocationArray = true;
    8168             :             }
    8169           1 :             else if ((longDimCount == 2 ||
    8170           6 :                       (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
    8171          10 :                      longDims[longDimCount - 2]->GetSize() == yDimSize &&
    8172          10 :                      longDims[longDimCount - 1]->GetSize() == xDimSize &&
    8173           1 :                      (latDimCount == 2 ||
    8174           6 :                       (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
    8175          15 :                      latDims[latDimCount - 2]->GetSize() == yDimSize &&
    8176           5 :                      latDims[latDimCount - 1]->GetSize() == xDimSize)
    8177             : 
    8178             :             {
    8179             :                 // Geolocation arrays are 2D (or 3D with first dimension of
    8180             :                 // size 1, as found in Sentinel 5P products), and of consistent
    8181             :                 // size with the variable
    8182           5 :                 useGeolocationArray = true;
    8183             :             }
    8184             :             else
    8185             :             {
    8186           0 :                 CPLDebug(
    8187             :                     "GDAL",
    8188             :                     "Longitude and latitude coordinate variables found, "
    8189             :                     "but their characteristics are not compatible of using "
    8190             :                     "them as geolocation arrays");
    8191             :             }
    8192           5 :             if (useGeolocationArray)
    8193             :             {
    8194          10 :                 CPLDebug("GDAL",
    8195             :                          "Setting geolocation array from variables %s and %s",
    8196           5 :                          poLongVar->GetName().c_str(),
    8197           5 :                          poLatVar->GetName().c_str());
    8198             :                 const std::string osFilenameLong =
    8199           5 :                     VSIMemGenerateHiddenFilename("longitude.tif");
    8200             :                 const std::string osFilenameLat =
    8201           5 :                     VSIMemGenerateHiddenFilename("latitude.tif");
    8202             :                 std::unique_ptr<GDALDataset> poTmpLongDS(
    8203             :                     longDimCount == 1
    8204           0 :                         ? poLongVar->AsClassicDataset(0, 0)
    8205          20 :                         : poLongVar->AsClassicDataset(longDimCount - 1,
    8206          15 :                                                       longDimCount - 2));
    8207           5 :                 auto hTIFFLongDS = GDALTranslate(
    8208             :                     osFilenameLong.c_str(),
    8209             :                     GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
    8210             :                 std::unique_ptr<GDALDataset> poTmpLatDS(
    8211           0 :                     latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
    8212          20 :                                      : poLatVar->AsClassicDataset(
    8213          15 :                                            latDimCount - 1, latDimCount - 2));
    8214           5 :                 auto hTIFFLatDS = GDALTranslate(
    8215             :                     osFilenameLat.c_str(),
    8216             :                     GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
    8217           5 :                 const bool bError =
    8218           5 :                     (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
    8219           5 :                 GDALClose(hTIFFLongDS);
    8220           5 :                 GDALClose(hTIFFLatDS);
    8221           5 :                 if (bError)
    8222             :                 {
    8223           0 :                     VSIUnlink(osFilenameLong.c_str());
    8224           0 :                     VSIUnlink(osFilenameLat.c_str());
    8225           0 :                     return nullptr;
    8226             :                 }
    8227             : 
    8228           5 :                 poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
    8229             :             }
    8230             :         }
    8231             :         else
    8232             :         {
    8233           0 :             CPLDebug("GDAL",
    8234             :                      "Coordinate variables available for %s, but "
    8235             :                      "longitude and/or latitude variables were not identified",
    8236           0 :                      poParent->GetName().c_str());
    8237             :         }
    8238             :     }
    8239             : 
    8240             :     // Build gdalwarp arguments
    8241          48 :     CPLStringList aosArgv;
    8242             : 
    8243          24 :     aosArgv.AddString("-of");
    8244          24 :     aosArgv.AddString("VRT");
    8245             : 
    8246          24 :     aosArgv.AddString("-r");
    8247          24 :     aosArgv.AddString(pszResampleAlg);
    8248             : 
    8249          24 :     if (!osDstWKT.empty())
    8250             :     {
    8251           2 :         aosArgv.AddString("-t_srs");
    8252           2 :         aosArgv.AddString(osDstWKT.c_str());
    8253             :     }
    8254             : 
    8255          24 :     if (useGeolocationArray)
    8256           5 :         aosArgv.AddString("-geoloc");
    8257             : 
    8258          24 :     if (gotXSpacing && gotYSpacing)
    8259             :     {
    8260           1 :         const double dfXMin = dfXStart - dfXSpacing / 2;
    8261             :         const double dfXMax =
    8262           1 :             dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
    8263           1 :         const double dfYMax = dfYStart - dfYSpacing / 2;
    8264             :         const double dfYMin =
    8265           1 :             dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
    8266           1 :         aosArgv.AddString("-te");
    8267           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
    8268           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
    8269           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
    8270           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
    8271             :     }
    8272             : 
    8273          24 :     if (poNewDimX && poNewDimY)
    8274             :     {
    8275           3 :         aosArgv.AddString("-ts");
    8276             :         aosArgv.AddString(
    8277           3 :             CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
    8278             :         aosArgv.AddString(
    8279           3 :             CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
    8280             :     }
    8281          21 :     else if (poNewDimX)
    8282             :     {
    8283           1 :         aosArgv.AddString("-ts");
    8284             :         aosArgv.AddString(
    8285           1 :             CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
    8286           1 :         aosArgv.AddString("0");
    8287             :     }
    8288          20 :     else if (poNewDimY)
    8289             :     {
    8290           1 :         aosArgv.AddString("-ts");
    8291           1 :         aosArgv.AddString("0");
    8292             :         aosArgv.AddString(
    8293           1 :             CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
    8294             :     }
    8295             : 
    8296             :     // Create a warped VRT dataset
    8297             :     GDALWarpAppOptions *psOptions =
    8298          24 :         GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
    8299          24 :     GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
    8300             :     std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
    8301          48 :         GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
    8302          24 :     GDALWarpAppOptionsFree(psOptions);
    8303          24 :     if (poReprojectedDS == nullptr)
    8304           3 :         return nullptr;
    8305             : 
    8306             :     int nBlockXSize;
    8307             :     int nBlockYSize;
    8308          21 :     poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    8309          21 :     anBlockSize.emplace_back(nBlockYSize);
    8310          21 :     anBlockSize.emplace_back(nBlockXSize);
    8311             : 
    8312          21 :     double adfGeoTransform[6] = {0, 0, 0, 0, 0, 0};
    8313          21 :     CPLErr eErr = poReprojectedDS->GetGeoTransform(adfGeoTransform);
    8314          21 :     CPLAssert(eErr == CE_None);
    8315          21 :     CPL_IGNORE_RET_VAL(eErr);
    8316             : 
    8317             :     auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
    8318           0 :         std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
    8319          42 :         poReprojectedDS->GetRasterYSize());
    8320             :     auto varY = GDALMDArrayRegularlySpaced::Create(
    8321          63 :         std::string(), poDimY->GetName(), poDimY,
    8322          84 :         adfGeoTransform[3] + adfGeoTransform[5] / 2, adfGeoTransform[5], 0);
    8323          21 :     poDimY->SetIndexingVariable(varY);
    8324             : 
    8325             :     auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
    8326           0 :         std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
    8327          42 :         poReprojectedDS->GetRasterXSize());
    8328             :     auto varX = GDALMDArrayRegularlySpaced::Create(
    8329          63 :         std::string(), poDimX->GetName(), poDimX,
    8330          84 :         adfGeoTransform[0] + adfGeoTransform[1] / 2, adfGeoTransform[1], 0);
    8331          21 :     poDimX->SetIndexingVariable(varX);
    8332             : 
    8333          21 :     apoNewDims.emplace_back(poDimY);
    8334          21 :     apoNewDims.emplace_back(poDimX);
    8335             :     auto newAr(std::shared_ptr<GDALMDArrayResampled>(
    8336          42 :         new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
    8337          21 :     newAr->SetSelf(newAr);
    8338          21 :     if (poTargetSRS)
    8339             :     {
    8340           2 :         newAr->m_poSRS.reset(poTargetSRS->Clone());
    8341             :     }
    8342             :     else
    8343             :     {
    8344          19 :         newAr->m_poSRS = poParent->GetSpatialRef();
    8345             :     }
    8346          21 :     newAr->m_poVarX = varX;
    8347          21 :     newAr->m_poVarY = varY;
    8348          21 :     newAr->m_poReprojectedDS = std::move(poReprojectedDS);
    8349          21 :     newAr->m_poParentDS = std::move(poParentDS);
    8350             : 
    8351             :     // If the input array is y,x,band ordered, the above newAr is
    8352             :     // actually band,y,x ordered as it is more convenient for
    8353             :     // GDALMDArrayResampled::IRead() implementation. But transpose that
    8354             :     // array to the order of the input array
    8355          21 :     if (bYXBandOrder)
    8356           4 :         return newAr->Transpose(std::vector<int>{1, 2, 0});
    8357             : 
    8358          19 :     return newAr;
    8359             : }
    8360             : 
    8361             : /************************************************************************/
    8362             : /*                   GDALMDArrayResampled::IRead()                      */
    8363             : /************************************************************************/
    8364             : 
    8365          29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
    8366             :                                  const size_t *count, const GInt64 *arrayStep,
    8367             :                                  const GPtrDiff_t *bufferStride,
    8368             :                                  const GDALExtendedDataType &bufferDataType,
    8369             :                                  void *pDstBuffer) const
    8370             : {
    8371          29 :     if (bufferDataType.GetClass() != GEDTC_NUMERIC)
    8372           0 :         return false;
    8373             : 
    8374             :     struct Stack
    8375             :     {
    8376             :         size_t nIters = 0;
    8377             :         GByte *dst_ptr = nullptr;
    8378             :         GPtrDiff_t dst_inc_offset = 0;
    8379             :     };
    8380             : 
    8381          29 :     const auto nDims = GetDimensionCount();
    8382          58 :     std::vector<Stack> stack(nDims + 1);  // +1 to avoid -Wnull-dereference
    8383          29 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    8384          92 :     for (size_t i = 0; i < nDims; i++)
    8385             :     {
    8386          63 :         stack[i].dst_inc_offset =
    8387          63 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    8388             :     }
    8389          29 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    8390             : 
    8391          29 :     size_t dimIdx = 0;
    8392          29 :     const size_t iDimY = nDims - 2;
    8393          29 :     const size_t iDimX = nDims - 1;
    8394             :     // Use an array to avoid a false positive warning from CLang Static
    8395             :     // Analyzer about flushCaches being never read
    8396          29 :     bool flushCaches[] = {false};
    8397             :     const bool bYXBandOrder =
    8398          29 :         m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
    8399             : 
    8400          38 : lbl_next_depth:
    8401          38 :     if (dimIdx == iDimY)
    8402             :     {
    8403          33 :         if (flushCaches[0])
    8404             :         {
    8405           5 :             flushCaches[0] = false;
    8406             :             // When changing of 2D slice, flush GDAL 2D buffers
    8407           5 :             m_poParentDS->FlushCache(false);
    8408           5 :             m_poReprojectedDS->FlushCache(false);
    8409             :         }
    8410             : 
    8411          33 :         if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
    8412             :                                     GF_Read, iDimX, iDimY, arrayStartIdx, count,
    8413             :                                     arrayStep, bufferStride, bufferDataType,
    8414          33 :                                     stack[dimIdx].dst_ptr))
    8415             :         {
    8416           0 :             return false;
    8417             :         }
    8418             :     }
    8419             :     else
    8420             :     {
    8421           5 :         stack[dimIdx].nIters = count[dimIdx];
    8422           5 :         if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
    8423           5 :             arrayStartIdx[dimIdx])
    8424             :         {
    8425           1 :             flushCaches[0] = true;
    8426             :         }
    8427           5 :         m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
    8428           5 :             arrayStartIdx[dimIdx];
    8429             :         while (true)
    8430             :         {
    8431           9 :             dimIdx++;
    8432           9 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    8433           9 :             goto lbl_next_depth;
    8434           9 :         lbl_return_to_caller:
    8435           9 :             dimIdx--;
    8436           9 :             if ((--stack[dimIdx].nIters) == 0)
    8437           5 :                 break;
    8438           4 :             flushCaches[0] = true;
    8439           4 :             ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
    8440           4 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    8441             :         }
    8442             :     }
    8443          38 :     if (dimIdx > 0)
    8444           9 :         goto lbl_return_to_caller;
    8445             : 
    8446          29 :     return true;
    8447             : }
    8448             : 
    8449             : /************************************************************************/
    8450             : /*                           GetResampled()                             */
    8451             : /************************************************************************/
    8452             : 
    8453             : /** Return an array that is a resampled / reprojected view of the current array
    8454             :  *
    8455             :  * This is the same as the C function GDALMDArrayGetResampled().
    8456             :  *
    8457             :  * Currently this method can only resample along the last 2 dimensions, unless
    8458             :  * orthorectifying a NASA EMIT dataset.
    8459             :  *
    8460             :  * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
    8461             :  * geometry lookup table (GLT) is used by default for fast orthorectification.
    8462             :  *
    8463             :  * Options available are:
    8464             :  * <ul>
    8465             :  * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
    8466             :  * Can be set to NO to use generic reprojection method.
    8467             :  * </li>
    8468             :  * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
    8469             :  * orthorectification to take into account the value of the
    8470             :  * /sensor_band_parameters/good_wavelengths array to decide if slices of the
    8471             :  * current array along the band dimension are valid.</li>
    8472             :  * </ul>
    8473             :  *
    8474             :  * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
    8475             :  *                   apoNewDims[i] can be NULL to let the method automatically
    8476             :  *                   determine it.
    8477             :  * @param resampleAlg Resampling algorithm
    8478             :  * @param poTargetSRS Target SRS, or nullptr
    8479             :  * @param papszOptions NULL-terminated list of options, or NULL.
    8480             :  *
    8481             :  * @return a new array, that holds a reference to the original one, and thus is
    8482             :  * a view of it (not a copy), or nullptr in case of error.
    8483             :  *
    8484             :  * @since 3.4
    8485             :  */
    8486          38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
    8487             :     const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
    8488             :     GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
    8489             :     CSLConstList papszOptions) const
    8490             : {
    8491          76 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    8492          38 :     if (!self)
    8493             :     {
    8494           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8495             :                  "Driver implementation issue: m_pSelf not set !");
    8496           0 :         return nullptr;
    8497             :     }
    8498          38 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    8499             :     {
    8500           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8501             :                  "GetResampled() only supports numeric data type");
    8502           0 :         return nullptr;
    8503             :     }
    8504             : 
    8505             :     // Special case for NASA EMIT datasets
    8506          76 :     auto apoDims = GetDimensions();
    8507          36 :     if (poTargetSRS == nullptr &&
    8508          59 :         ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
    8509          20 :           apoDims[1]->GetName() == "crosstrack" &&
    8510          10 :           apoDims[2]->GetName() == "bands" &&
    8511          48 :           (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
    8512           1 :            apoNewDims ==
    8513          42 :                std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
    8514          30 :                                                            apoDims[2]})) ||
    8515          51 :          (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
    8516           3 :           apoDims[1]->GetName() == "crosstrack" &&
    8517          77 :           apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
    8518          13 :         CPLTestBool(CSLFetchNameValueDef(papszOptions,
    8519             :                                          "EMIT_ORTHORECTIFICATION", "YES")))
    8520             :     {
    8521           9 :         auto poRootGroup = GetRootGroup();
    8522           9 :         if (poRootGroup)
    8523             :         {
    8524          18 :             auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
    8525          18 :             auto poLocationGroup = poRootGroup->OpenGroup("location");
    8526           9 :             if (poAttrGeotransform &&
    8527           9 :                 poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
    8528           9 :                 poAttrGeotransform->GetDimensionCount() == 1 &&
    8529          27 :                 poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
    8530           9 :                 poLocationGroup)
    8531             :             {
    8532          18 :                 auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
    8533          18 :                 auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
    8534          27 :                 if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
    8535          18 :                     poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
    8536          18 :                     poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
    8537          27 :                     poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
    8538          27 :                     poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
    8539           9 :                     poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
    8540             :                 {
    8541             :                     return CreateGLTOrthorectified(
    8542             :                         self, poRootGroup, poGLT_X, poGLT_Y,
    8543             :                         /* nGLTIndexOffset = */ -1,
    8544          18 :                         poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
    8545             :                 }
    8546             :             }
    8547             :         }
    8548             :     }
    8549             : 
    8550          29 :     if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
    8551             :                                          "EMIT_ORTHORECTIFICATION", "NO")))
    8552             :     {
    8553           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8554             :                  "EMIT_ORTHORECTIFICATION required, but dataset and/or "
    8555             :                  "parameters are not compatible with it");
    8556           0 :         return nullptr;
    8557             :     }
    8558             : 
    8559             :     return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
    8560          29 :                                         poTargetSRS, papszOptions);
    8561             : }
    8562             : 
    8563             : /************************************************************************/
    8564             : /*                         GDALDatasetFromArray()                       */
    8565             : /************************************************************************/
    8566             : 
    8567             : class GDALDatasetFromArray;
    8568             : 
    8569             : namespace
    8570             : {
    8571             : struct MetadataItem
    8572             : {
    8573             :     std::shared_ptr<GDALMDArray> poArray{};
    8574             :     std::string osName{};
    8575             :     std::string osDefinition{};
    8576             :     bool bDefinitionUsesPctForG = false;
    8577             : };
    8578             : 
    8579             : struct BandImageryMetadata
    8580             : {
    8581             :     std::shared_ptr<GDALMDArray> poCentralWavelengthArray{};
    8582             :     double dfCentralWavelengthToMicrometer = 1.0;
    8583             :     std::shared_ptr<GDALMDArray> poFWHMArray{};
    8584             :     double dfFWHMToMicrometer = 1.0;
    8585             : };
    8586             : 
    8587             : }  // namespace
    8588             : 
    8589             : class GDALRasterBandFromArray final : public GDALPamRasterBand
    8590             : {
    8591             :     std::vector<GUInt64> m_anOffset{};
    8592             :     std::vector<size_t> m_anCount{};
    8593             :     std::vector<GPtrDiff_t> m_anStride{};
    8594             : 
    8595             :   protected:
    8596             :     CPLErr IReadBlock(int, int, void *) override;
    8597             :     CPLErr IWriteBlock(int, int, void *) override;
    8598             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
    8599             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
    8600             :                      GDALDataType eBufType, GSpacing nPixelSpaceBuf,
    8601             :                      GSpacing nLineSpaceBuf,
    8602             :                      GDALRasterIOExtraArg *psExtraArg) override;
    8603             : 
    8604             :   public:
    8605             :     explicit GDALRasterBandFromArray(
    8606             :         GDALDatasetFromArray *poDSIn,
    8607             :         const std::vector<GUInt64> &anOtherDimCoord,
    8608             :         const std::vector<std::vector<MetadataItem>>
    8609             :             &aoBandParameterMetadataItems,
    8610             :         const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
    8611             :         double dfDelay, time_t nStartTime, bool &bHasWarned);
    8612             : 
    8613             :     double GetNoDataValue(int *pbHasNoData) override;
    8614             :     int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
    8615             :     uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
    8616             :     double GetOffset(int *pbHasOffset) override;
    8617             :     double GetScale(int *pbHasScale) override;
    8618             :     const char *GetUnitType() override;
    8619             :     GDALColorInterp GetColorInterpretation() override;
    8620             : };
    8621             : 
    8622             : class GDALDatasetFromArray final : public GDALPamDataset
    8623             : {
    8624             :     friend class GDALRasterBandFromArray;
    8625             : 
    8626             :     std::shared_ptr<GDALMDArray> m_poArray;
    8627             :     size_t m_iXDim;
    8628             :     size_t m_iYDim;
    8629             :     double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
    8630             :     bool m_bHasGT = false;
    8631             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
    8632             :     GDALMultiDomainMetadata m_oMDD{};
    8633             :     std::string m_osOvrFilename{};
    8634             : 
    8635             :   public:
    8636         203 :     GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
    8637             :                          size_t iXDim, size_t iYDim)
    8638         203 :         : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
    8639             :     {
    8640             :         // Initialize an overview filename from the filename of the array
    8641             :         // and its name.
    8642         203 :         const std::string &osFilename = m_poArray->GetFilename();
    8643         203 :         if (!osFilename.empty())
    8644             :         {
    8645         181 :             m_osOvrFilename = osFilename;
    8646         181 :             m_osOvrFilename += '.';
    8647        6630 :             for (char ch : m_poArray->GetName())
    8648             :             {
    8649        6449 :                 if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
    8650        5715 :                     (ch >= 'a' && ch <= 'z') || ch == '_')
    8651             :                 {
    8652        5188 :                     m_osOvrFilename += ch;
    8653             :                 }
    8654             :                 else
    8655             :                 {
    8656        1261 :                     m_osOvrFilename += '_';
    8657             :                 }
    8658             :             }
    8659         181 :             m_osOvrFilename += ".ovr";
    8660         181 :             oOvManager.Initialize(this);
    8661             :         }
    8662         203 :     }
    8663             : 
    8664             :     static GDALDatasetFromArray *
    8665             :     Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
    8666             :            size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
    8667             :            CSLConstList papszOptions);
    8668             : 
    8669         406 :     ~GDALDatasetFromArray()
    8670         203 :     {
    8671         203 :         GDALDatasetFromArray::Close();
    8672         406 :     }
    8673             : 
    8674         332 :     CPLErr Close() override
    8675             :     {
    8676         332 :         CPLErr eErr = CE_None;
    8677         332 :         if (nOpenFlags != OPEN_FLAGS_CLOSED)
    8678             :         {
    8679         332 :             if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
    8680             :                 CE_None)
    8681           0 :                 eErr = CE_Failure;
    8682         332 :             m_poArray.reset();
    8683             :         }
    8684         332 :         return eErr;
    8685             :     }
    8686             : 
    8687          54 :     CPLErr GetGeoTransform(double *padfGeoTransform) override
    8688             :     {
    8689          54 :         memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
    8690          54 :         return m_bHasGT ? CE_None : CE_Failure;
    8691             :     }
    8692             : 
    8693          62 :     const OGRSpatialReference *GetSpatialRef() const override
    8694             :     {
    8695          62 :         if (m_poArray->GetDimensionCount() < 2)
    8696           3 :             return nullptr;
    8697          59 :         m_poSRS = m_poArray->GetSpatialRef();
    8698          59 :         if (m_poSRS)
    8699             :         {
    8700          20 :             m_poSRS.reset(m_poSRS->Clone());
    8701          40 :             auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
    8702          60 :             for (auto &m : axisMapping)
    8703             :             {
    8704          40 :                 if (m == static_cast<int>(m_iXDim) + 1)
    8705          20 :                     m = 1;
    8706          20 :                 else if (m == static_cast<int>(m_iYDim) + 1)
    8707          20 :                     m = 2;
    8708             :             }
    8709          20 :             m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
    8710             :         }
    8711          59 :         return m_poSRS.get();
    8712             :     }
    8713             : 
    8714           5 :     CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
    8715             :     {
    8716           5 :         return m_oMDD.SetMetadata(papszMetadata, pszDomain);
    8717             :     }
    8718             : 
    8719         163 :     char **GetMetadata(const char *pszDomain) override
    8720             :     {
    8721         163 :         return m_oMDD.GetMetadata(pszDomain);
    8722             :     }
    8723             : 
    8724         233 :     const char *GetMetadataItem(const char *pszName,
    8725             :                                 const char *pszDomain) override
    8726             :     {
    8727         421 :         if (!m_osOvrFilename.empty() && pszName &&
    8728         435 :             EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
    8729          14 :             EQUAL(pszDomain, "OVERVIEWS"))
    8730             :         {
    8731          14 :             return m_osOvrFilename.c_str();
    8732             :         }
    8733         219 :         return m_oMDD.GetMetadataItem(pszName, pszDomain);
    8734             :     }
    8735             : };
    8736             : 
    8737             : /************************************************************************/
    8738             : /*                      GDALRasterBandFromArray()                       */
    8739             : /************************************************************************/
    8740             : 
    8741         275 : GDALRasterBandFromArray::GDALRasterBandFromArray(
    8742             :     GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
    8743             :     const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
    8744             :     const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
    8745         275 :     double dfDelay, time_t nStartTime, bool &bHasWarned)
    8746             : {
    8747         275 :     const auto &poArray(poDSIn->m_poArray);
    8748         275 :     const auto &dims(poArray->GetDimensions());
    8749         275 :     const auto nDimCount(dims.size());
    8750         550 :     const auto blockSize(poArray->GetBlockSize());
    8751         263 :     nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
    8752         538 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    8753         138 :                                                   blockSize[poDSIn->m_iYDim]))
    8754             :                       : 1;
    8755         275 :     nBlockXSize = blockSize[poDSIn->m_iXDim]
    8756         150 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    8757         150 :                                                   blockSize[poDSIn->m_iXDim]))
    8758         275 :                       : poDSIn->GetRasterXSize();
    8759         275 :     eDataType = poArray->GetDataType().GetNumericDataType();
    8760         275 :     eAccess = poDSIn->eAccess;
    8761         275 :     m_anOffset.resize(nDimCount);
    8762         275 :     m_anCount.resize(nDimCount, 1);
    8763         275 :     m_anStride.resize(nDimCount);
    8764         935 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    8765             :     {
    8766         660 :         if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
    8767             :         {
    8768         244 :             std::string dimName(dims[i]->GetName());
    8769         122 :             GUInt64 nIndex = anOtherDimCoord[j];
    8770             :             // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
    8771             :             // subsetted dimensions as generated by GetView()
    8772         122 :             if (STARTS_WITH(dimName.c_str(), "subset_"))
    8773             :             {
    8774             :                 CPLStringList aosTokens(
    8775          12 :                     CSLTokenizeString2(dimName.c_str(), "_", 0));
    8776           6 :                 if (aosTokens.size() == 5)
    8777             :                 {
    8778           6 :                     dimName = aosTokens[1];
    8779          18 :                     const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
    8780           6 :                         aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
    8781           6 :                     const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
    8782           6 :                     nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
    8783           0 :                                           : nStartDim - (nIndex * -nIncrDim);
    8784             :                 }
    8785             :             }
    8786         122 :             if (nDimCount != 3 || dimName != "Band")
    8787             :             {
    8788          66 :                 SetMetadataItem(
    8789             :                     CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
    8790             :                     CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
    8791             :             }
    8792             : 
    8793         122 :             auto indexingVar = dims[i]->GetIndexingVariable();
    8794             : 
    8795             :             // If the indexing variable is also listed in band parameter arrays,
    8796             :             // then don't use our default formatting
    8797         122 :             if (indexingVar)
    8798             :             {
    8799          38 :                 for (const auto &oItem : aoBandParameterMetadataItems[j])
    8800             :                 {
    8801          12 :                     if (oItem.poArray->GetFullName() ==
    8802          12 :                         indexingVar->GetFullName())
    8803             :                     {
    8804          12 :                         indexingVar.reset();
    8805          12 :                         break;
    8806             :                     }
    8807             :                 }
    8808             :             }
    8809             : 
    8810         148 :             if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
    8811          26 :                 indexingVar->GetDimensions()[0]->GetSize() ==
    8812          26 :                     dims[i]->GetSize())
    8813             :             {
    8814          26 :                 if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
    8815             :                 {
    8816           0 :                     if (!bHasWarned)
    8817             :                     {
    8818           0 :                         CPLError(
    8819             :                             CE_Warning, CPLE_AppDefined,
    8820             :                             "Maximum delay to load band metadata from "
    8821             :                             "dimension indexing variables has expired. "
    8822             :                             "Increase the value of the "
    8823             :                             "LOAD_EXTRA_DIM_METADATA_DELAY "
    8824             :                             "option of GDALMDArray::AsClassicDataset() "
    8825             :                             "(also accessible as the "
    8826             :                             "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
    8827             :                             "configuration option), "
    8828             :                             "or set it to 'unlimited' for unlimited delay. ");
    8829           0 :                         bHasWarned = true;
    8830             :                     }
    8831             :                 }
    8832             :                 else
    8833             :                 {
    8834          26 :                     size_t nCount = 1;
    8835          26 :                     const auto &dt(indexingVar->GetDataType());
    8836          52 :                     std::vector<GByte> abyTmp(dt.GetSize());
    8837          52 :                     if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
    8838          26 :                                           nullptr, nullptr, dt, &abyTmp[0]))
    8839             :                     {
    8840          26 :                         char *pszTmp = nullptr;
    8841          26 :                         GDALExtendedDataType::CopyValue(
    8842          26 :                             &abyTmp[0], dt, &pszTmp,
    8843          52 :                             GDALExtendedDataType::CreateString());
    8844          26 :                         if (pszTmp)
    8845             :                         {
    8846          26 :                             SetMetadataItem(
    8847             :                                 CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
    8848             :                                 pszTmp);
    8849          26 :                             CPLFree(pszTmp);
    8850             :                         }
    8851             : 
    8852          26 :                         const auto &unit(indexingVar->GetUnit());
    8853          26 :                         if (!unit.empty())
    8854             :                         {
    8855          12 :                             SetMetadataItem(
    8856             :                                 CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
    8857             :                                 unit.c_str());
    8858             :                         }
    8859             :                     }
    8860             :                 }
    8861             :             }
    8862             : 
    8863         138 :             for (const auto &oItem : aoBandParameterMetadataItems[j])
    8864             :             {
    8865          32 :                 CPLString osVal;
    8866             : 
    8867          16 :                 size_t nCount = 1;
    8868          16 :                 const auto &dt(oItem.poArray->GetDataType());
    8869          16 :                 if (oItem.bDefinitionUsesPctForG)
    8870             :                 {
    8871             :                     // There is one and only one %[x][.y]f|g in osDefinition
    8872          12 :                     std::vector<GByte> abyTmp(dt.GetSize());
    8873          12 :                     if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
    8874           6 :                                             nullptr, nullptr, dt, &abyTmp[0]))
    8875             :                     {
    8876           6 :                         double dfVal = 0;
    8877           6 :                         GDALExtendedDataType::CopyValue(
    8878           6 :                             &abyTmp[0], dt, &dfVal,
    8879          12 :                             GDALExtendedDataType::Create(GDT_Float64));
    8880           6 :                         osVal.Printf(oItem.osDefinition.c_str(), dfVal);
    8881             :                     }
    8882             :                 }
    8883             :                 else
    8884             :                 {
    8885             :                     // There should be zero or one %s in osDefinition
    8886          10 :                     char *pszValue = nullptr;
    8887          10 :                     if (dt.GetClass() == GEDTC_STRING)
    8888             :                     {
    8889           4 :                         CPL_IGNORE_RET_VAL(oItem.poArray->Read(
    8890           2 :                             &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
    8891             :                             dt, &pszValue));
    8892             :                     }
    8893             :                     else
    8894             :                     {
    8895          16 :                         std::vector<GByte> abyTmp(dt.GetSize());
    8896          16 :                         if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
    8897             :                                                 nullptr, nullptr, dt,
    8898           8 :                                                 &abyTmp[0]))
    8899             :                         {
    8900           8 :                             GDALExtendedDataType::CopyValue(
    8901           8 :                                 &abyTmp[0], dt, &pszValue,
    8902          16 :                                 GDALExtendedDataType::CreateString());
    8903             :                         }
    8904             :                     }
    8905             : 
    8906          10 :                     if (pszValue)
    8907             :                     {
    8908          10 :                         osVal.Printf(oItem.osDefinition.c_str(), pszValue);
    8909          10 :                         CPLFree(pszValue);
    8910             :                     }
    8911             :                 }
    8912          16 :                 if (!osVal.empty())
    8913          16 :                     SetMetadataItem(oItem.osName.c_str(), osVal);
    8914             :             }
    8915             : 
    8916         122 :             if (aoBandImageryMetadata[j].poCentralWavelengthArray)
    8917             :             {
    8918             :                 auto &poCentralWavelengthArray =
    8919           2 :                     aoBandImageryMetadata[j].poCentralWavelengthArray;
    8920           2 :                 size_t nCount = 1;
    8921           2 :                 const auto &dt(poCentralWavelengthArray->GetDataType());
    8922           4 :                 std::vector<GByte> abyTmp(dt.GetSize());
    8923           4 :                 if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
    8924             :                                                    &nCount, nullptr, nullptr,
    8925           2 :                                                    dt, &abyTmp[0]))
    8926             :                 {
    8927           2 :                     double dfVal = 0;
    8928           2 :                     GDALExtendedDataType::CopyValue(
    8929           2 :                         &abyTmp[0], dt, &dfVal,
    8930           4 :                         GDALExtendedDataType::Create(GDT_Float64));
    8931           2 :                     SetMetadataItem(
    8932             :                         "CENTRAL_WAVELENGTH_UM",
    8933             :                         CPLSPrintf(
    8934           2 :                             "%g", dfVal * aoBandImageryMetadata[j]
    8935           2 :                                               .dfCentralWavelengthToMicrometer),
    8936             :                         "IMAGERY");
    8937             :                 }
    8938             :             }
    8939             : 
    8940         122 :             if (aoBandImageryMetadata[j].poFWHMArray)
    8941             :             {
    8942           2 :                 auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
    8943           2 :                 size_t nCount = 1;
    8944           2 :                 const auto &dt(poFWHMArray->GetDataType());
    8945           4 :                 std::vector<GByte> abyTmp(dt.GetSize());
    8946           4 :                 if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
    8947           2 :                                       nullptr, dt, &abyTmp[0]))
    8948             :                 {
    8949           2 :                     double dfVal = 0;
    8950           2 :                     GDALExtendedDataType::CopyValue(
    8951           2 :                         &abyTmp[0], dt, &dfVal,
    8952           4 :                         GDALExtendedDataType::Create(GDT_Float64));
    8953           2 :                     SetMetadataItem(
    8954             :                         "FWHM_UM",
    8955           2 :                         CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
    8956           2 :                                                      .dfFWHMToMicrometer),
    8957             :                         "IMAGERY");
    8958             :                 }
    8959             :             }
    8960             : 
    8961         122 :             m_anOffset[i] = anOtherDimCoord[j];
    8962         122 :             j++;
    8963             :         }
    8964             :     }
    8965         275 : }
    8966             : 
    8967             : /************************************************************************/
    8968             : /*                           GetNoDataValue()                           */
    8969             : /************************************************************************/
    8970             : 
    8971         109 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
    8972             : {
    8973         109 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8974         109 :     const auto &poArray(l_poDS->m_poArray);
    8975         109 :     bool bHasNodata = false;
    8976         109 :     const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
    8977         109 :     if (pbHasNoData)
    8978          97 :         *pbHasNoData = bHasNodata;
    8979         109 :     return res;
    8980             : }
    8981             : 
    8982             : /************************************************************************/
    8983             : /*                       GetNoDataValueAsInt64()                        */
    8984             : /************************************************************************/
    8985             : 
    8986           1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
    8987             : {
    8988           1 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8989           1 :     const auto &poArray(l_poDS->m_poArray);
    8990           1 :     bool bHasNodata = false;
    8991           1 :     const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
    8992           1 :     if (pbHasNoData)
    8993           1 :         *pbHasNoData = bHasNodata;
    8994           1 :     return nodata;
    8995             : }
    8996             : 
    8997             : /************************************************************************/
    8998             : /*                      GetNoDataValueAsUInt64()                        */
    8999             : /************************************************************************/
    9000             : 
    9001           1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
    9002             : {
    9003           1 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9004           1 :     const auto &poArray(l_poDS->m_poArray);
    9005           1 :     bool bHasNodata = false;
    9006           1 :     const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
    9007           1 :     if (pbHasNoData)
    9008           1 :         *pbHasNoData = bHasNodata;
    9009           1 :     return nodata;
    9010             : }
    9011             : 
    9012             : /************************************************************************/
    9013             : /*                             GetOffset()                              */
    9014             : /************************************************************************/
    9015             : 
    9016          38 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
    9017             : {
    9018          38 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9019          38 :     const auto &poArray(l_poDS->m_poArray);
    9020          38 :     bool bHasValue = false;
    9021          38 :     double dfRes = poArray->GetOffset(&bHasValue);
    9022          38 :     if (pbHasOffset)
    9023          19 :         *pbHasOffset = bHasValue;
    9024          38 :     return dfRes;
    9025             : }
    9026             : 
    9027             : /************************************************************************/
    9028             : /*                           GetUnitType()                              */
    9029             : /************************************************************************/
    9030             : 
    9031          44 : const char *GDALRasterBandFromArray::GetUnitType()
    9032             : {
    9033          44 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9034          44 :     const auto &poArray(l_poDS->m_poArray);
    9035          44 :     return poArray->GetUnit().c_str();
    9036             : }
    9037             : 
    9038             : /************************************************************************/
    9039             : /*                             GetScale()                              */
    9040             : /************************************************************************/
    9041             : 
    9042          36 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
    9043             : {
    9044          36 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9045          36 :     const auto &poArray(l_poDS->m_poArray);
    9046          36 :     bool bHasValue = false;
    9047          36 :     double dfRes = poArray->GetScale(&bHasValue);
    9048          36 :     if (pbHasScale)
    9049          17 :         *pbHasScale = bHasValue;
    9050          36 :     return dfRes;
    9051             : }
    9052             : 
    9053             : /************************************************************************/
    9054             : /*                            IReadBlock()                              */
    9055             : /************************************************************************/
    9056             : 
    9057          68 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
    9058             :                                            void *pImage)
    9059             : {
    9060          68 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    9061          68 :     const int nXOff = nBlockXOff * nBlockXSize;
    9062          68 :     const int nYOff = nBlockYOff * nBlockYSize;
    9063          68 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    9064          68 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    9065             :     GDALRasterIOExtraArg sExtraArg;
    9066          68 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    9067         136 :     return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    9068             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    9069         136 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    9070             : }
    9071             : 
    9072             : /************************************************************************/
    9073             : /*                            IWriteBlock()                             */
    9074             : /************************************************************************/
    9075             : 
    9076           1 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
    9077             :                                             void *pImage)
    9078             : {
    9079           1 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    9080           1 :     const int nXOff = nBlockXOff * nBlockXSize;
    9081           1 :     const int nYOff = nBlockYOff * nBlockYSize;
    9082           1 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    9083           1 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    9084             :     GDALRasterIOExtraArg sExtraArg;
    9085           1 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    9086           2 :     return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    9087             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    9088           2 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    9089             : }
    9090             : 
    9091             : /************************************************************************/
    9092             : /*                            IRasterIO()                               */
    9093             : /************************************************************************/
    9094             : 
    9095         328 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
    9096             :                                           int nYOff, int nXSize, int nYSize,
    9097             :                                           void *pData, int nBufXSize,
    9098             :                                           int nBufYSize, GDALDataType eBufType,
    9099             :                                           GSpacing nPixelSpaceBuf,
    9100             :                                           GSpacing nLineSpaceBuf,
    9101             :                                           GDALRasterIOExtraArg *psExtraArg)
    9102             : {
    9103         328 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9104         328 :     const auto &poArray(l_poDS->m_poArray);
    9105         328 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
    9106         328 :     if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
    9107         328 :         (nPixelSpaceBuf % nBufferDTSize) == 0 &&
    9108         328 :         (nLineSpaceBuf % nBufferDTSize) == 0)
    9109             :     {
    9110         328 :         m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
    9111         328 :         m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
    9112         656 :         m_anStride[l_poDS->m_iXDim] =
    9113         328 :             static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
    9114         328 :         if (poArray->GetDimensionCount() >= 2)
    9115             :         {
    9116         319 :             m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
    9117         319 :             m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
    9118         319 :             m_anStride[l_poDS->m_iYDim] =
    9119         319 :                 static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
    9120             :         }
    9121         328 :         if (eRWFlag == GF_Read)
    9122             :         {
    9123         644 :             return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
    9124         322 :                                  m_anStride.data(),
    9125         644 :                                  GDALExtendedDataType::Create(eBufType), pData)
    9126         322 :                        ? CE_None
    9127         322 :                        : CE_Failure;
    9128             :         }
    9129             :         else
    9130             :         {
    9131          12 :             return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
    9132           6 :                                   m_anStride.data(),
    9133          12 :                                   GDALExtendedDataType::Create(eBufType), pData)
    9134           6 :                        ? CE_None
    9135           6 :                        : CE_Failure;
    9136             :         }
    9137             :     }
    9138           0 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    9139             :                                      pData, nBufXSize, nBufYSize, eBufType,
    9140           0 :                                      nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
    9141             : }
    9142             : 
    9143             : /************************************************************************/
    9144             : /*                      GetColorInterpretation()                        */
    9145             : /************************************************************************/
    9146             : 
    9147          58 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
    9148             : {
    9149          58 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9150          58 :     const auto &poArray(l_poDS->m_poArray);
    9151         174 :     auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
    9152          58 :     if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
    9153             :     {
    9154           6 :         bool bOK = false;
    9155           6 :         GUInt64 nStartIndex = 0;
    9156           6 :         if (poArray->GetDimensionCount() == 2 &&
    9157           0 :             poAttr->GetDimensionCount() == 0)
    9158             :         {
    9159           0 :             bOK = true;
    9160             :         }
    9161           6 :         else if (poArray->GetDimensionCount() == 3)
    9162             :         {
    9163           6 :             uint64_t nExtraDimSamples = 1;
    9164           6 :             const auto &apoDims = poArray->GetDimensions();
    9165          24 :             for (size_t i = 0; i < apoDims.size(); ++i)
    9166             :             {
    9167          18 :                 if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
    9168           6 :                     nExtraDimSamples *= apoDims[i]->GetSize();
    9169             :             }
    9170           6 :             if (poAttr->GetDimensionsSize() ==
    9171          12 :                 std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
    9172             :             {
    9173           6 :                 bOK = true;
    9174             :             }
    9175           6 :             nStartIndex = nBand - 1;
    9176             :         }
    9177           6 :         if (bOK)
    9178             :         {
    9179           6 :             const auto oStringDT = GDALExtendedDataType::CreateString();
    9180           6 :             const size_t nCount = 1;
    9181           6 :             const GInt64 arrayStep = 1;
    9182           6 :             const GPtrDiff_t bufferStride = 1;
    9183           6 :             char *pszValue = nullptr;
    9184           6 :             poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
    9185           6 :                          oStringDT, &pszValue);
    9186           6 :             if (pszValue)
    9187             :             {
    9188             :                 const auto eColorInterp =
    9189           6 :                     GDALGetColorInterpretationByName(pszValue);
    9190           6 :                 CPLFree(pszValue);
    9191           6 :                 return eColorInterp;
    9192             :             }
    9193             :         }
    9194             :     }
    9195          52 :     return GCI_Undefined;
    9196             : }
    9197             : 
    9198             : /************************************************************************/
    9199             : /*                    GDALDatasetFromArray::Create()                    */
    9200             : /************************************************************************/
    9201             : 
    9202         236 : GDALDatasetFromArray *GDALDatasetFromArray::Create(
    9203             :     const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
    9204             :     const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
    9205             : 
    9206             : {
    9207         236 :     const auto nDimCount(array->GetDimensionCount());
    9208         236 :     if (nDimCount == 0)
    9209             :     {
    9210           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    9211             :                  "Unsupported number of dimensions");
    9212           1 :         return nullptr;
    9213             :     }
    9214         469 :     if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
    9215         234 :         array->GetDataType().GetNumericDataType() == GDT_Unknown)
    9216             :     {
    9217           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    9218             :                  "Only arrays with numeric data types "
    9219             :                  "can be exposed as classic GDALDataset");
    9220           1 :         return nullptr;
    9221             :     }
    9222         234 :     if (iXDim >= nDimCount ||
    9223         219 :         (nDimCount >= 2 && (iYDim >= nDimCount || iXDim == iYDim)))
    9224             :     {
    9225           6 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
    9226           6 :         return nullptr;
    9227             :     }
    9228         228 :     GUInt64 nTotalBands = 1;
    9229         228 :     const auto &dims(array->GetDimensions());
    9230         739 :     for (size_t i = 0; i < nDimCount; ++i)
    9231             :     {
    9232         512 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9233             :         {
    9234          70 :             if (dims[i]->GetSize() > 65536 / nTotalBands)
    9235             :             {
    9236           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9237             :                          "Too many bands. Operate on a sliced view");
    9238           1 :                 return nullptr;
    9239             :             }
    9240          69 :             nTotalBands *= dims[i]->GetSize();
    9241             :         }
    9242             :     }
    9243             : 
    9244         454 :     std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
    9245         738 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    9246             :     {
    9247         511 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9248             :         {
    9249          69 :             oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
    9250          69 :             ++j;
    9251             :         }
    9252             :     }
    9253             : 
    9254         227 :     const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
    9255             : 
    9256             :     const char *pszBandMetadata =
    9257         227 :         CSLFetchNameValue(papszOptions, "BAND_METADATA");
    9258             :     std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
    9259         454 :         nNewDimCount);
    9260         227 :     if (pszBandMetadata)
    9261             :     {
    9262          21 :         if (!poRootGroup)
    9263             :         {
    9264           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9265             :                      "Root group should be provided when BAND_METADATA is set");
    9266          14 :             return nullptr;
    9267             :         }
    9268          20 :         CPLJSONDocument oDoc;
    9269          20 :         if (!oDoc.LoadMemory(pszBandMetadata))
    9270             :         {
    9271           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9272             :                      "Invalid JSON content for BAND_METADATA");
    9273           1 :             return nullptr;
    9274             :         }
    9275          19 :         auto oRoot = oDoc.GetRoot();
    9276          19 :         if (oRoot.GetType() != CPLJSONObject::Type::Array)
    9277             :         {
    9278           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9279             :                      "Value of BAND_METADATA should be an array");
    9280           1 :             return nullptr;
    9281             :         }
    9282             : 
    9283          18 :         auto oArray = oRoot.ToArray();
    9284          26 :         for (int j = 0; j < oArray.Size(); ++j)
    9285             :         {
    9286          19 :             const auto oJsonItem = oArray[j];
    9287          19 :             MetadataItem oItem;
    9288             : 
    9289          38 :             auto osBandArrayFullname = oJsonItem.GetString("array");
    9290          19 :             if (osBandArrayFullname.empty())
    9291             :             {
    9292           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9293             :                          "BAND_METADATA[%d][\"array\"] is missing", j);
    9294           1 :                 return nullptr;
    9295             :             }
    9296             :             oItem.poArray =
    9297          18 :                 poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
    9298          18 :             if (!oItem.poArray)
    9299             :             {
    9300           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9301             :                          "Array %s cannot be found",
    9302             :                          osBandArrayFullname.c_str());
    9303           1 :                 return nullptr;
    9304             :             }
    9305          17 :             if (oItem.poArray->GetDimensionCount() != 1)
    9306             :             {
    9307           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9308             :                          "Array %s is not a 1D array",
    9309             :                          osBandArrayFullname.c_str());
    9310           1 :                 return nullptr;
    9311             :             }
    9312             :             const auto &osAuxArrayDimName =
    9313          16 :                 oItem.poArray->GetDimensions()[0]->GetName();
    9314          16 :             auto oIter = oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
    9315          16 :             if (oIter == oMapArrayDimNameToExtraDimIdx.end())
    9316             :             {
    9317           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9318             :                          "Dimension %s of array %s is not a non-X/Y dimension "
    9319             :                          "of array %s",
    9320             :                          osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
    9321           1 :                          array->GetName().c_str());
    9322           1 :                 return nullptr;
    9323             :             }
    9324          15 :             const size_t iExtraDimIdx = oIter->second;
    9325          15 :             CPLAssert(iExtraDimIdx < nNewDimCount);
    9326             : 
    9327          15 :             oItem.osName = oJsonItem.GetString("item_name");
    9328          15 :             if (oItem.osName.empty())
    9329             :             {
    9330           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9331             :                          "BAND_METADATA[%d][\"item_name\"] is missing", j);
    9332           1 :                 return nullptr;
    9333             :             }
    9334             : 
    9335          28 :             const auto osDefinition = oJsonItem.GetString("item_value", "%s");
    9336             : 
    9337             :             // Check correctness of definition
    9338          14 :             bool bFirstNumericFormatter = true;
    9339          14 :             std::string osModDefinition;
    9340          14 :             bool bDefinitionUsesPctForG = false;
    9341          72 :             for (size_t k = 0; k < osDefinition.size(); ++k)
    9342             :             {
    9343          64 :                 if (osDefinition[k] == '%')
    9344             :                 {
    9345          13 :                     osModDefinition += osDefinition[k];
    9346          13 :                     if (k + 1 == osDefinition.size())
    9347             :                     {
    9348           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9349             :                                  "Value of "
    9350             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9351             :                                  "%s is invalid at offset %d",
    9352             :                                  osAuxArrayDimName.c_str(), j,
    9353             :                                  osDefinition.c_str(), int(k));
    9354           1 :                         return nullptr;
    9355             :                     }
    9356          12 :                     ++k;
    9357          12 :                     if (osDefinition[k] == '%')
    9358             :                     {
    9359           1 :                         osModDefinition += osDefinition[k];
    9360           1 :                         continue;
    9361             :                     }
    9362          11 :                     if (!bFirstNumericFormatter)
    9363             :                     {
    9364           1 :                         CPLError(
    9365             :                             CE_Failure, CPLE_AppDefined,
    9366             :                             "Value of "
    9367             :                             "BAND_METADATA[\"%s\"][%d][\"item_value\"] = %s is "
    9368             :                             "invalid at offset %d: %%[x][.y]f|g or %%s "
    9369             :                             "formatters should be specified at most once",
    9370             :                             osAuxArrayDimName.c_str(), j, osDefinition.c_str(),
    9371             :                             int(k));
    9372           1 :                         return nullptr;
    9373             :                     }
    9374          10 :                     bFirstNumericFormatter = false;
    9375          13 :                     for (; k < osDefinition.size(); ++k)
    9376             :                     {
    9377          13 :                         osModDefinition += osDefinition[k];
    9378          26 :                         if (!((osDefinition[k] >= '0' &&
    9379          12 :                                osDefinition[k] <= '9') ||
    9380          11 :                               osDefinition[k] == '.'))
    9381          10 :                             break;
    9382             :                     }
    9383          20 :                     if (k == osDefinition.size() ||
    9384          10 :                         (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
    9385           5 :                          osDefinition[k] != 's'))
    9386             :                     {
    9387           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9388             :                                  "Value of "
    9389             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9390             :                                  "%s is invalid at offset %d: only "
    9391             :                                  "%%[x][.y]f|g or %%s formatters are accepted",
    9392             :                                  osAuxArrayDimName.c_str(), j,
    9393             :                                  osDefinition.c_str(), int(k));
    9394           1 :                         return nullptr;
    9395             :                     }
    9396           9 :                     bDefinitionUsesPctForG =
    9397           9 :                         (osDefinition[k] == 'f' || osDefinition[k] == 'g');
    9398           9 :                     if (bDefinitionUsesPctForG)
    9399             :                     {
    9400           5 :                         if (oItem.poArray->GetDataType().GetClass() !=
    9401             :                             GEDTC_NUMERIC)
    9402             :                         {
    9403           1 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9404             :                                      "Data type of %s array is not numeric",
    9405             :                                      osAuxArrayDimName.c_str());
    9406           1 :                             return nullptr;
    9407             :                         }
    9408             :                     }
    9409             :                 }
    9410          56 :                 else if (osDefinition[k] == '$' &&
    9411          56 :                          k + 1 < osDefinition.size() &&
    9412           5 :                          osDefinition[k + 1] == '{')
    9413             :                 {
    9414           5 :                     const auto nPos = osDefinition.find('}', k);
    9415           5 :                     if (nPos == std::string::npos)
    9416             :                     {
    9417           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9418             :                                  "Value of "
    9419             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9420             :                                  "%s is invalid at offset %d",
    9421             :                                  osAuxArrayDimName.c_str(), j,
    9422             :                                  osDefinition.c_str(), int(k));
    9423           2 :                         return nullptr;
    9424             :                     }
    9425             :                     const auto osAttrName =
    9426           4 :                         osDefinition.substr(k + 2, nPos - (k + 2));
    9427           4 :                     auto poAttr = oItem.poArray->GetAttribute(osAttrName);
    9428           4 :                     if (!poAttr)
    9429             :                     {
    9430           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9431             :                                  "Value of "
    9432             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9433             :                                  "%s is invalid: %s is not an attribute of %s",
    9434             :                                  osAuxArrayDimName.c_str(), j,
    9435             :                                  osDefinition.c_str(), osAttrName.c_str(),
    9436             :                                  osAuxArrayDimName.c_str());
    9437           1 :                         return nullptr;
    9438             :                     }
    9439           3 :                     k = nPos;
    9440           3 :                     const char *pszValue = poAttr->ReadAsString();
    9441           3 :                     if (!pszValue)
    9442             :                     {
    9443           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9444             :                                  "Cannot get value of attribute %s of %s as a "
    9445             :                                  "string",
    9446             :                                  osAttrName.c_str(), osAuxArrayDimName.c_str());
    9447           0 :                         return nullptr;
    9448             :                     }
    9449           3 :                     osModDefinition += pszValue;
    9450             :                 }
    9451             :                 else
    9452             :                 {
    9453          46 :                     osModDefinition += osDefinition[k];
    9454             :                 }
    9455             :             }
    9456             : 
    9457           8 :             oItem.osDefinition = std::move(osModDefinition);
    9458           8 :             oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
    9459             : 
    9460           8 :             aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
    9461           8 :                 std::move(oItem));
    9462             :         }
    9463             :     }
    9464             : 
    9465         426 :     std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
    9466             :     const char *pszBandImageryMetadata =
    9467         213 :         CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
    9468         213 :     if (pszBandImageryMetadata)
    9469             :     {
    9470          12 :         if (!poRootGroup)
    9471             :         {
    9472           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9473             :                      "Root group should be provided when BAND_IMAGERY_METADATA "
    9474             :                      "is set");
    9475          10 :             return nullptr;
    9476             :         }
    9477          11 :         CPLJSONDocument oDoc;
    9478          11 :         if (!oDoc.LoadMemory(pszBandImageryMetadata))
    9479             :         {
    9480           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9481             :                      "Invalid JSON content for BAND_IMAGERY_METADATA");
    9482           1 :             return nullptr;
    9483             :         }
    9484          10 :         auto oRoot = oDoc.GetRoot();
    9485          10 :         if (oRoot.GetType() != CPLJSONObject::Type::Object)
    9486             :         {
    9487           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9488             :                      "Value of BAND_IMAGERY_METADATA should be an object");
    9489           1 :             return nullptr;
    9490             :         }
    9491          12 :         for (const auto &oJsonItem : oRoot.GetChildren())
    9492             :         {
    9493          22 :             if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
    9494          12 :                 oJsonItem.GetName() == "FWHM_UM")
    9495             :             {
    9496          18 :                 auto osBandArrayFullname = oJsonItem.GetString("array");
    9497           9 :                 if (osBandArrayFullname.empty())
    9498             :                 {
    9499           1 :                     CPLError(
    9500             :                         CE_Failure, CPLE_AppDefined,
    9501             :                         "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] is missing",
    9502           2 :                         oJsonItem.GetName().c_str());
    9503           1 :                     return nullptr;
    9504             :                 }
    9505             :                 auto poArray =
    9506           8 :                     poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
    9507           8 :                 if (!poArray)
    9508             :                 {
    9509           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9510             :                              "Array %s cannot be found",
    9511             :                              osBandArrayFullname.c_str());
    9512           1 :                     return nullptr;
    9513             :                 }
    9514           7 :                 if (poArray->GetDimensionCount() != 1)
    9515             :                 {
    9516           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9517             :                              "Array %s is not a 1D array",
    9518             :                              osBandArrayFullname.c_str());
    9519           1 :                     return nullptr;
    9520             :                 }
    9521             :                 const auto &osAuxArrayDimName =
    9522           6 :                     poArray->GetDimensions()[0]->GetName();
    9523             :                 auto oIter =
    9524           6 :                     oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
    9525           6 :                 if (oIter == oMapArrayDimNameToExtraDimIdx.end())
    9526             :                 {
    9527           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9528             :                              "Dimension \"%s\" of array \"%s\" is not a "
    9529             :                              "non-X/Y dimension of array \"%s\"",
    9530             :                              osAuxArrayDimName.c_str(),
    9531             :                              osBandArrayFullname.c_str(),
    9532           1 :                              array->GetName().c_str());
    9533           1 :                     return nullptr;
    9534             :                 }
    9535           5 :                 const size_t iExtraDimIdx = oIter->second;
    9536           5 :                 CPLAssert(iExtraDimIdx < nNewDimCount);
    9537             : 
    9538          10 :                 std::string osUnit = oJsonItem.GetString("unit", "um");
    9539           5 :                 if (STARTS_WITH(osUnit.c_str(), "${"))
    9540             :                 {
    9541           3 :                     if (osUnit.back() != '}')
    9542             :                     {
    9543           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9544             :                                  "Value of "
    9545             :                                  "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
    9546             :                                  "%s is invalid",
    9547           2 :                                  oJsonItem.GetName().c_str(), osUnit.c_str());
    9548           2 :                         return nullptr;
    9549             :                     }
    9550           2 :                     const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
    9551           2 :                     auto poAttr = poArray->GetAttribute(osAttrName);
    9552           2 :                     if (!poAttr)
    9553             :                     {
    9554           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9555             :                                  "Value of "
    9556             :                                  "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
    9557             :                                  "%s is invalid: %s is not an attribute of %s",
    9558           2 :                                  oJsonItem.GetName().c_str(), osUnit.c_str(),
    9559             :                                  osAttrName.c_str(),
    9560             :                                  osBandArrayFullname.c_str());
    9561           1 :                         return nullptr;
    9562             :                     }
    9563           1 :                     const char *pszValue = poAttr->ReadAsString();
    9564           1 :                     if (!pszValue)
    9565             :                     {
    9566           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9567             :                                  "Cannot get value of attribute %s of %s as a "
    9568             :                                  "string",
    9569             :                                  osAttrName.c_str(),
    9570             :                                  osBandArrayFullname.c_str());
    9571           0 :                         return nullptr;
    9572             :                     }
    9573           1 :                     osUnit = pszValue;
    9574             :                 }
    9575           3 :                 double dfConvToUM = 1.0;
    9576           7 :                 if (osUnit == "nm" || osUnit == "nanometre" ||
    9577           9 :                     osUnit == "nanometres" || osUnit == "nanometer" ||
    9578           2 :                     osUnit == "nanometers")
    9579             :                 {
    9580           1 :                     dfConvToUM = 1e-3;
    9581             :                 }
    9582           3 :                 else if (!(osUnit == "um" || osUnit == "micrometre" ||
    9583           1 :                            osUnit == "micrometres" || osUnit == "micrometer" ||
    9584           1 :                            osUnit == "micrometers"))
    9585             :                 {
    9586           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9587             :                              "Unhandled value for "
    9588             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
    9589           2 :                              oJsonItem.GetName().c_str(), osUnit.c_str());
    9590           1 :                     return nullptr;
    9591             :                 }
    9592             : 
    9593           2 :                 BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
    9594           2 :                 if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
    9595             :                 {
    9596           1 :                     item.poCentralWavelengthArray = std::move(poArray);
    9597           1 :                     item.dfCentralWavelengthToMicrometer = dfConvToUM;
    9598             :                 }
    9599             :                 else
    9600             :                 {
    9601           1 :                     item.poFWHMArray = std::move(poArray);
    9602           1 :                     item.dfFWHMToMicrometer = dfConvToUM;
    9603             :                 }
    9604             :             }
    9605             :             else
    9606             :             {
    9607           1 :                 CPLError(CE_Warning, CPLE_AppDefined,
    9608             :                          "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
    9609           2 :                          oJsonItem.GetName().c_str());
    9610             :             }
    9611             :         }
    9612             :     }
    9613             : 
    9614         406 :     auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
    9615             : 
    9616         203 :     poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
    9617             : 
    9618         203 :     poDS->nRasterYSize =
    9619         203 :         nDimCount < 2 ? 1
    9620         191 :                       : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    9621         191 :                                                   dims[iYDim]->GetSize()));
    9622         406 :     poDS->nRasterXSize = static_cast<int>(
    9623         203 :         std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
    9624             : 
    9625         406 :     std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
    9626         406 :     std::vector<GUInt64> anStackIters(nDimCount);
    9627         406 :     std::vector<size_t> anMapNewToOld(nNewDimCount);
    9628         642 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    9629             :     {
    9630         439 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9631             :         {
    9632          45 :             anMapNewToOld[j] = i;
    9633          45 :             j++;
    9634             :         }
    9635             :     }
    9636             : 
    9637         406 :     poDS->m_bHasGT =
    9638         203 :         array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_adfGeoTransform);
    9639             : 
    9640         406 :     const auto attrs(array->GetAttributes());
    9641         306 :     for (const auto &attr : attrs)
    9642             :     {
    9643         103 :         if (attr->GetName() != "COLOR_INTERPRETATION")
    9644             :         {
    9645         188 :             auto stringArray = attr->ReadAsStringArray();
    9646         188 :             std::string val;
    9647          94 :             if (stringArray.size() > 1)
    9648             :             {
    9649          38 :                 val += '{';
    9650             :             }
    9651         226 :             for (int i = 0; i < stringArray.size(); ++i)
    9652             :             {
    9653         132 :                 if (i > 0)
    9654          38 :                     val += ',';
    9655         132 :                 val += stringArray[i];
    9656             :             }
    9657          94 :             if (stringArray.size() > 1)
    9658             :             {
    9659          38 :                 val += '}';
    9660             :             }
    9661          94 :             poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
    9662             :         }
    9663             :     }
    9664             : 
    9665         203 :     const char *pszDelay = CSLFetchNameValueDef(
    9666             :         papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
    9667             :         CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
    9668             :     const double dfDelay =
    9669         203 :         EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
    9670         203 :     const auto nStartTime = time(nullptr);
    9671         203 :     bool bHasWarned = false;
    9672             :     // Instantiate bands by iterating over non-XY variables
    9673         203 :     size_t iDim = 0;
    9674         203 :     int nCurBand = 1;
    9675         322 : lbl_next_depth:
    9676         322 :     if (iDim < nNewDimCount)
    9677             :     {
    9678          47 :         anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
    9679          47 :         anOtherDimCoord[iDim] = 0;
    9680             :         while (true)
    9681             :         {
    9682         119 :             ++iDim;
    9683         119 :             goto lbl_next_depth;
    9684         119 :         lbl_return_to_caller:
    9685         119 :             --iDim;
    9686         119 :             --anStackIters[iDim];
    9687         119 :             if (anStackIters[iDim] == 0)
    9688          47 :                 break;
    9689          72 :             ++anOtherDimCoord[iDim];
    9690             :         }
    9691             :     }
    9692             :     else
    9693             :     {
    9694         550 :         poDS->SetBand(nCurBand,
    9695             :                       new GDALRasterBandFromArray(
    9696         275 :                           poDS.get(), anOtherDimCoord,
    9697             :                           aoBandParameterMetadataItems, aoBandImageryMetadata,
    9698         275 :                           dfDelay, nStartTime, bHasWarned));
    9699         275 :         ++nCurBand;
    9700             :     }
    9701         322 :     if (iDim > 0)
    9702         119 :         goto lbl_return_to_caller;
    9703             : 
    9704         203 :     if (!array->GetFilename().empty())
    9705             :     {
    9706         181 :         poDS->SetPhysicalFilename(array->GetFilename().c_str());
    9707             :         std::string osDerivedDatasetName(
    9708             :             CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
    9709         362 :                        int(iYDim), array->GetFullName().c_str()));
    9710         181 :         if (!array->GetContext().empty())
    9711             :         {
    9712           2 :             osDerivedDatasetName += " with context ";
    9713           2 :             osDerivedDatasetName += array->GetContext();
    9714             :         }
    9715         181 :         poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
    9716         181 :         poDS->TryLoadXML();
    9717             : 
    9718           2 :         for (const auto &[pszKey, pszValue] :
    9719             :              cpl::IterateNameValue(static_cast<CSLConstList>(
    9720         183 :                  poDS->GDALPamDataset::GetMetadata())))
    9721             :         {
    9722           1 :             poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
    9723             :         }
    9724             :     }
    9725             : 
    9726         203 :     return poDS.release();
    9727             : }
    9728             : 
    9729             : /************************************************************************/
    9730             : /*                          AsClassicDataset()                         */
    9731             : /************************************************************************/
    9732             : 
    9733             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
    9734             :  *
    9735             :  * In the case of > 2D arrays, additional dimensions will be represented as
    9736             :  * raster bands.
    9737             :  *
    9738             :  * The "reverse" method is GDALRasterBand::AsMDArray().
    9739             :  *
    9740             :  * This is the same as the C function GDALMDArrayAsClassicDataset().
    9741             :  *
    9742             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
    9743             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
    9744             :  *              Ignored if the dimension count is 1.
    9745             :  * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
    9746             :  *                    and BAND_IMAGERY_METADATA option.
    9747             :  * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
    9748             :  *                     nullptr. Current supported options are:
    9749             :  *                     <ul>
    9750             :  *                     <li>BAND_METADATA: JSON serialized array defining which
    9751             :  *                         arrays of the poRootGroup, indexed by non-X and Y
    9752             :  *                         dimensions, should be mapped as band metadata items.
    9753             :  *                         Each array item should be an object with the
    9754             :  *                         following members:
    9755             :  *                         - "array": full name of a band parameter array.
    9756             :  *                           Such array must be a one
    9757             :  *                           dimensional array, and its dimension must be one of
    9758             :  *                           the dimensions of the array on which the method is
    9759             :  *                           called (excluding the X and Y dimensons).
    9760             :  *                         - "item_name": band metadata item name
    9761             :  *                         - "item_value": (optional) String, where "%[x][.y]f",
    9762             :  *                           "%[x][.y]g" or "%s" printf-like formatting can be
    9763             :  *                           used to format the corresponding value of the
    9764             :  *                           parameter array. The percentage character should be
    9765             :  *                           repeated: "%%"
    9766             :  *                           "${attribute_name}" can also be used to include the
    9767             :  *                           value of an attribute for the array.
    9768             :  *                           If "item_value" is not provided, a default formatting
    9769             :  *                           of the value will be applied.
    9770             :  *
    9771             :  *                         Example:
    9772             :  *                         [
    9773             :  *                            {
    9774             :  *                              "array": "/sensor_band_parameters/wavelengths",
    9775             :  *                              "item_name": "WAVELENGTH",
    9776             :  *                              "item_value": "%.1f ${units}"
    9777             :  *                            },
    9778             :  *                            {
    9779             :  *                              "array": "/sensor_band_parameters/fwhm",
    9780             :  *                              "item_name": "FWHM"
    9781             :  *                            },
    9782             :  *                            {
    9783             :  *                              "array": "/sensor_band_parameters/fwhm",
    9784             :  *                              "item_name": "FWHM_UNIT",
    9785             :  *                              "item_value": "${units}"
    9786             :  *                            }
    9787             :  *                         ]
    9788             :  *                     </li>
    9789             :  *                     <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
    9790             :  *                         JSON serialized object defining which arrays of the
    9791             :  *                         poRootGroup, indexed by non-X and Y dimensions,
    9792             :  *                         should be mapped as band metadata items in the
    9793             :  *                         band IMAGERY domain.
    9794             :  *                         The object currently accepts 2 members:
    9795             :  *                         - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
    9796             :  *                           micrometers.
    9797             :  *                         - "FWHM_UM": Full-width half-maximum
    9798             :  *                           in micrometers.
    9799             :  *                         The value of each member should be an object with the
    9800             :  *                         following members:
    9801             :  *                         - "array": (required) full name of a band parameter
    9802             :  *                           array.
    9803             :  *                           Such array must be a one dimensional array, and its
    9804             :  *                           dimension must be one of the dimensions of the
    9805             :  *                           array on which the method is called
    9806             :  *                           (excluding the X and Y dimensons).
    9807             :  *                         - "unit": (optional) unit of the values pointed in
    9808             :  *                           the above array.
    9809             :  *                           Can be a literal string or a string of the form
    9810             :  *                           "${attribute_name}" to point to an attribute for
    9811             :  *                           the array.
    9812             :  *                           Accepted values are "um", "micrometer"
    9813             :  *                           (with UK vs US spelling, singular or plural), "nm",
    9814             :  *                           "nanometer" (with UK vs US spelling, singular or
    9815             :  *                           plural)
    9816             :  *                           If not provided, micrometer is assumed.
    9817             :  *
    9818             :  *                         Example for EMIT datasets:
    9819             :  *                         {
    9820             :  *                            "CENTRAL_WAVELENGTH_UM": {
    9821             :  *                                "array": "/sensor_band_parameters/wavelengths",
    9822             :  *                                "unit": "${units}"
    9823             :  *                            },
    9824             :  *                            "FWHM_UM": {
    9825             :  *                                "array": "/sensor_band_parameters/fwhm",
    9826             :  *                                "unit": "${units}"
    9827             :  *                            }
    9828             :  *                         }
    9829             :  *                     </li>
    9830             :  *                     <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
    9831             :  *                         seconds allowed to set the DIM_{dimname}_VALUE band
    9832             :  *                         metadata items from the indexing variable of the
    9833             :  *                         dimensions.
    9834             :  *                         Default value is 5. 'unlimited' can be used to mean
    9835             :  *                         unlimited delay. Can also be defined globally with
    9836             :  *                         the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
    9837             :  *                         option.</li>
    9838             :  *                     </ul>
    9839             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
    9840             :  */
    9841             : GDALDataset *
    9842         236 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
    9843             :                               const std::shared_ptr<GDALGroup> &poRootGroup,
    9844             :                               CSLConstList papszOptions) const
    9845             : {
    9846         472 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    9847         236 :     if (!self)
    9848             :     {
    9849           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    9850             :                  "Driver implementation issue: m_pSelf not set !");
    9851           0 :         return nullptr;
    9852             :     }
    9853         236 :     return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
    9854         236 :                                         papszOptions);
    9855             : }
    9856             : 
    9857             : /************************************************************************/
    9858             : /*                           GetStatistics()                            */
    9859             : /************************************************************************/
    9860             : 
    9861             : /**
    9862             :  * \brief Fetch statistics.
    9863             :  *
    9864             :  * Returns the minimum, maximum, mean and standard deviation of all
    9865             :  * pixel values in this array.
    9866             :  *
    9867             :  * If bForce is FALSE results will only be returned if it can be done
    9868             :  * quickly (i.e. without scanning the data).  If bForce is FALSE and
    9869             :  * results cannot be returned efficiently, the method will return CE_Warning
    9870             :  * but no warning will have been issued.   This is a non-standard use of
    9871             :  * the CE_Warning return value to indicate "nothing done".
    9872             :  *
    9873             :  * When cached statistics are not available, and bForce is TRUE,
    9874             :  * ComputeStatistics() is called.
    9875             :  *
    9876             :  * Note that file formats using PAM (Persistent Auxiliary Metadata) services
    9877             :  * will generally cache statistics in the .aux.xml file allowing fast fetch
    9878             :  * after the first request.
    9879             :  *
    9880             :  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
    9881             :  *
    9882             :  * This method is the same as the C function GDALMDArrayGetStatistics().
    9883             :  *
    9884             :  * @param bApproxOK Currently ignored. In the future, should be set to true
    9885             :  * if statistics on the whole array are wished, or to false if a subset of it
    9886             :  * may be used.
    9887             :  *
    9888             :  * @param bForce If false statistics will only be returned if it can
    9889             :  * be done without rescanning the image.
    9890             :  *
    9891             :  * @param pdfMin Location into which to load image minimum (may be NULL).
    9892             :  *
    9893             :  * @param pdfMax Location into which to load image maximum (may be NULL).-
    9894             :  *
    9895             :  * @param pdfMean Location into which to load image mean (may be NULL).
    9896             :  *
    9897             :  * @param pdfStdDev Location into which to load image standard deviation
    9898             :  * (may be NULL).
    9899             :  *
    9900             :  * @param pnValidCount Number of samples whose value is different from the
    9901             :  * nodata value. (may be NULL)
    9902             :  *
    9903             :  * @param pfnProgress a function to call to report progress, or NULL.
    9904             :  *
    9905             :  * @param pProgressData application data to pass to the progress function.
    9906             :  *
    9907             :  * @return CE_None on success, CE_Warning if no values returned,
    9908             :  * CE_Failure if an error occurs.
    9909             :  *
    9910             :  * @since GDAL 3.2
    9911             :  */
    9912             : 
    9913          10 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
    9914             :                                   double *pdfMax, double *pdfMean,
    9915             :                                   double *pdfStdDev, GUInt64 *pnValidCount,
    9916             :                                   GDALProgressFunc pfnProgress,
    9917             :                                   void *pProgressData)
    9918             : {
    9919          10 :     if (!bForce)
    9920           1 :         return CE_Warning;
    9921             : 
    9922          18 :     return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
    9923           9 :                              pnValidCount, pfnProgress, pProgressData, nullptr)
    9924           9 :                ? CE_None
    9925           9 :                : CE_Failure;
    9926             : }
    9927             : 
    9928             : /************************************************************************/
    9929             : /*                         ComputeStatistics()                          */
    9930             : /************************************************************************/
    9931             : 
    9932             : /**
    9933             :  * \brief Compute statistics.
    9934             :  *
    9935             :  * Returns the minimum, maximum, mean and standard deviation of all
    9936             :  * pixel values in this array.
    9937             :  *
    9938             :  * Pixels taken into account in statistics are those whose mask value
    9939             :  * (as determined by GetMask()) is non-zero.
    9940             :  *
    9941             :  * Once computed, the statistics will generally be "set" back on the
    9942             :  * owing dataset.
    9943             :  *
    9944             :  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
    9945             :  *
    9946             :  * This method is the same as the C functions GDALMDArrayComputeStatistics().
    9947             :  * and GDALMDArrayComputeStatisticsEx().
    9948             :  *
    9949             :  * @param bApproxOK Currently ignored. In the future, should be set to true
    9950             :  * if statistics on the whole array are wished, or to false if a subset of it
    9951             :  * may be used.
    9952             :  *
    9953             :  * @param pdfMin Location into which to load image minimum (may be NULL).
    9954             :  *
    9955             :  * @param pdfMax Location into which to load image maximum (may be NULL).-
    9956             :  *
    9957             :  * @param pdfMean Location into which to load image mean (may be NULL).
    9958             :  *
    9959             :  * @param pdfStdDev Location into which to load image standard deviation
    9960             :  * (may be NULL).
    9961             :  *
    9962             :  * @param pnValidCount Number of samples whose value is different from the
    9963             :  * nodata value. (may be NULL)
    9964             :  *
    9965             :  * @param pfnProgress a function to call to report progress, or NULL.
    9966             :  *
    9967             :  * @param pProgressData application data to pass to the progress function.
    9968             :  *
    9969             :  * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
    9970             :  *                     Options are driver specific. For now the netCDF and Zarr
    9971             :  *                     drivers recognize UPDATE_METADATA=YES, whose effect is
    9972             :  *                     to add or update the actual_range attribute with the
    9973             :  *                     computed min/max, only if done on the full array, in non
    9974             :  *                     approximate mode, and the dataset is opened in update
    9975             :  *                     mode.
    9976             :  *
    9977             :  * @return true on success
    9978             :  *
    9979             :  * @since GDAL 3.2
    9980             :  */
    9981             : 
    9982          13 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
    9983             :                                     double *pdfMax, double *pdfMean,
    9984             :                                     double *pdfStdDev, GUInt64 *pnValidCount,
    9985             :                                     GDALProgressFunc pfnProgress,
    9986             :                                     void *pProgressData,
    9987             :                                     CSLConstList papszOptions)
    9988             : {
    9989             :     struct StatsPerChunkType
    9990             :     {
    9991             :         const GDALMDArray *array = nullptr;
    9992             :         std::shared_ptr<GDALMDArray> poMask{};
    9993             :         double dfMin = cpl::NumericLimits<double>::max();
    9994             :         double dfMax = -cpl::NumericLimits<double>::max();
    9995             :         double dfMean = 0.0;
    9996             :         double dfM2 = 0.0;
    9997             :         GUInt64 nValidCount = 0;
    9998             :         std::vector<GByte> abyData{};
    9999             :         std::vector<double> adfData{};
   10000             :         std::vector<GByte> abyMaskData{};
   10001             :         GDALProgressFunc pfnProgress = nullptr;
   10002             :         void *pProgressData = nullptr;
   10003             :     };
   10004             : 
   10005          13 :     const auto PerChunkFunc = [](GDALAbstractMDArray *,
   10006             :                                  const GUInt64 *chunkArrayStartIdx,
   10007             :                                  const size_t *chunkCount, GUInt64 iCurChunk,
   10008             :                                  GUInt64 nChunkCount, void *pUserData)
   10009             :     {
   10010          13 :         StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
   10011          13 :         const GDALMDArray *array = data->array;
   10012          13 :         const GDALMDArray *poMask = data->poMask.get();
   10013          13 :         const size_t nDims = array->GetDimensionCount();
   10014          13 :         size_t nVals = 1;
   10015          34 :         for (size_t i = 0; i < nDims; i++)
   10016          21 :             nVals *= chunkCount[i];
   10017             : 
   10018             :         // Get mask
   10019          13 :         data->abyMaskData.resize(nVals);
   10020          13 :         if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
   10021          13 :                            poMask->GetDataType(), &data->abyMaskData[0])))
   10022             :         {
   10023           0 :             return false;
   10024             :         }
   10025             : 
   10026             :         // Get data
   10027          13 :         const auto &oType = array->GetDataType();
   10028          13 :         if (oType.GetNumericDataType() == GDT_Float64)
   10029             :         {
   10030           6 :             data->adfData.resize(nVals);
   10031           6 :             if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
   10032           6 :                              oType, &data->adfData[0]))
   10033             :             {
   10034           0 :                 return false;
   10035             :             }
   10036             :         }
   10037             :         else
   10038             :         {
   10039           7 :             data->abyData.resize(nVals * oType.GetSize());
   10040           7 :             if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
   10041           7 :                              oType, &data->abyData[0]))
   10042             :             {
   10043           0 :                 return false;
   10044             :             }
   10045           7 :             data->adfData.resize(nVals);
   10046           7 :             GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
   10047           7 :                             static_cast<int>(oType.GetSize()),
   10048           7 :                             &data->adfData[0], GDT_Float64,
   10049             :                             static_cast<int>(sizeof(double)),
   10050             :                             static_cast<GPtrDiff_t>(nVals));
   10051             :         }
   10052         912 :         for (size_t i = 0; i < nVals; i++)
   10053             :         {
   10054         899 :             if (data->abyMaskData[i])
   10055             :             {
   10056         894 :                 const double dfValue = data->adfData[i];
   10057         894 :                 data->dfMin = std::min(data->dfMin, dfValue);
   10058         894 :                 data->dfMax = std::max(data->dfMax, dfValue);
   10059         894 :                 data->nValidCount++;
   10060         894 :                 const double dfDelta = dfValue - data->dfMean;
   10061         894 :                 data->dfMean += dfDelta / data->nValidCount;
   10062         894 :                 data->dfM2 += dfDelta * (dfValue - data->dfMean);
   10063             :             }
   10064             :         }
   10065          13 :         if (data->pfnProgress &&
   10066           0 :             !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
   10067             :                                "", data->pProgressData))
   10068             :         {
   10069           0 :             return false;
   10070             :         }
   10071          13 :         return true;
   10072             :     };
   10073             : 
   10074          13 :     const auto &oType = GetDataType();
   10075          26 :     if (oType.GetClass() != GEDTC_NUMERIC ||
   10076          13 :         GDALDataTypeIsComplex(oType.GetNumericDataType()))
   10077             :     {
   10078           0 :         CPLError(
   10079             :             CE_Failure, CPLE_NotSupported,
   10080             :             "Statistics can only be computed on non-complex numeric data type");
   10081           0 :         return false;
   10082             :     }
   10083             : 
   10084          13 :     const size_t nDims = GetDimensionCount();
   10085          26 :     std::vector<GUInt64> arrayStartIdx(nDims);
   10086          26 :     std::vector<GUInt64> count(nDims);
   10087          13 :     const auto &poDims = GetDimensions();
   10088          34 :     for (size_t i = 0; i < nDims; i++)
   10089             :     {
   10090          21 :         count[i] = poDims[i]->GetSize();
   10091             :     }
   10092          13 :     const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
   10093             :     const size_t nMaxChunkSize =
   10094             :         pszSwathSize
   10095          13 :             ? static_cast<size_t>(
   10096           0 :                   std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
   10097           0 :                            CPLAtoGIntBig(pszSwathSize)))
   10098             :             : static_cast<size_t>(
   10099          13 :                   std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
   10100          13 :                            GDALGetCacheMax64() / 4));
   10101          26 :     StatsPerChunkType sData;
   10102          13 :     sData.array = this;
   10103          13 :     sData.poMask = GetMask(nullptr);
   10104          13 :     if (sData.poMask == nullptr)
   10105             :     {
   10106           0 :         return false;
   10107             :     }
   10108          13 :     sData.pfnProgress = pfnProgress;
   10109          13 :     sData.pProgressData = pProgressData;
   10110          13 :     if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
   10111          26 :                          GetProcessingChunkSize(nMaxChunkSize).data(),
   10112          13 :                          PerChunkFunc, &sData))
   10113             :     {
   10114           0 :         return false;
   10115             :     }
   10116             : 
   10117          13 :     if (pdfMin)
   10118          13 :         *pdfMin = sData.dfMin;
   10119             : 
   10120          13 :     if (pdfMax)
   10121          13 :         *pdfMax = sData.dfMax;
   10122             : 
   10123          13 :     if (pdfMean)
   10124          11 :         *pdfMean = sData.dfMean;
   10125             : 
   10126             :     const double dfStdDev =
   10127          13 :         sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
   10128          13 :     if (pdfStdDev)
   10129          11 :         *pdfStdDev = dfStdDev;
   10130             : 
   10131          13 :     if (pnValidCount)
   10132          11 :         *pnValidCount = sData.nValidCount;
   10133             : 
   10134          13 :     SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
   10135          13 :                   sData.nValidCount, papszOptions);
   10136             : 
   10137          13 :     return true;
   10138             : }
   10139             : 
   10140             : /************************************************************************/
   10141             : /*                            SetStatistics()                           */
   10142             : /************************************************************************/
   10143             : //! @cond Doxygen_Suppress
   10144           5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
   10145             :                                 double /* dfMax */, double /* dfMean */,
   10146             :                                 double /* dfStdDev */,
   10147             :                                 GUInt64 /* nValidCount */,
   10148             :                                 CSLConstList /* papszOptions */)
   10149             : {
   10150           5 :     CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
   10151           5 :     return false;
   10152             : }
   10153             : 
   10154             : //! @endcond
   10155             : 
   10156             : /************************************************************************/
   10157             : /*                           ClearStatistics()                          */
   10158             : /************************************************************************/
   10159             : 
   10160             : /**
   10161             :  * \brief Clear statistics.
   10162             :  *
   10163             :  * @since GDAL 3.4
   10164             :  */
   10165           0 : void GDALMDArray::ClearStatistics()
   10166             : {
   10167           0 : }
   10168             : 
   10169             : /************************************************************************/
   10170             : /*                      GetCoordinateVariables()                        */
   10171             : /************************************************************************/
   10172             : 
   10173             : /**
   10174             :  * \brief Return coordinate variables.
   10175             :  *
   10176             :  * Coordinate variables are an alternate way of indexing an array that can
   10177             :  * be sometimes used. For example, an array collected through remote sensing
   10178             :  * might be indexed by (scanline, pixel). But there can be
   10179             :  * a longitude and latitude arrays alongside that are also both indexed by
   10180             :  * (scanline, pixel), and are referenced from operational arrays for
   10181             :  * reprojection purposes.
   10182             :  *
   10183             :  * For netCDF, this will return the arrays referenced by the "coordinates"
   10184             :  * attribute.
   10185             :  *
   10186             :  * This method is the same as the C function
   10187             :  * GDALMDArrayGetCoordinateVariables().
   10188             :  *
   10189             :  * @return a vector of arrays
   10190             :  *
   10191             :  * @since GDAL 3.4
   10192             :  */
   10193             : 
   10194             : std::vector<std::shared_ptr<GDALMDArray>>
   10195          13 : GDALMDArray::GetCoordinateVariables() const
   10196             : {
   10197          13 :     return {};
   10198             : }
   10199             : 
   10200             : /************************************************************************/
   10201             : /*                       ~GDALExtendedDataType()                        */
   10202             : /************************************************************************/
   10203             : 
   10204             : GDALExtendedDataType::~GDALExtendedDataType() = default;
   10205             : 
   10206             : /************************************************************************/
   10207             : /*                        GDALExtendedDataType()                        */
   10208             : /************************************************************************/
   10209             : 
   10210        9520 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
   10211        9520 :                                            GDALExtendedDataTypeSubType eSubType)
   10212             :     : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
   10213        9520 :       m_nMaxStringLength(nMaxStringLength)
   10214             : {
   10215        9520 : }
   10216             : 
   10217             : /************************************************************************/
   10218             : /*                        GDALExtendedDataType()                        */
   10219             : /************************************************************************/
   10220             : 
   10221       37220 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
   10222             :     : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
   10223       37220 :       m_nSize(GDALGetDataTypeSizeBytes(eType))
   10224             : {
   10225       37220 : }
   10226             : 
   10227             : /************************************************************************/
   10228             : /*                        GDALExtendedDataType()                        */
   10229             : /************************************************************************/
   10230             : 
   10231          63 : GDALExtendedDataType::GDALExtendedDataType(
   10232             :     const std::string &osName, GDALDataType eBaseType,
   10233          63 :     std::unique_ptr<GDALRasterAttributeTable> poRAT)
   10234             :     : m_osName(osName), m_eClass(GEDTC_NUMERIC), m_eNumericDT(eBaseType),
   10235          63 :       m_nSize(GDALGetDataTypeSizeBytes(eBaseType)), m_poRAT(std::move(poRAT))
   10236             : {
   10237          63 : }
   10238             : 
   10239             : /************************************************************************/
   10240             : /*                        GDALExtendedDataType()                        */
   10241             : /************************************************************************/
   10242             : 
   10243         849 : GDALExtendedDataType::GDALExtendedDataType(
   10244             :     const std::string &osName, size_t nTotalSize,
   10245         849 :     std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
   10246             :     : m_osName(osName), m_eClass(GEDTC_COMPOUND),
   10247         849 :       m_aoComponents(std::move(components)), m_nSize(nTotalSize)
   10248             : {
   10249         849 : }
   10250             : 
   10251             : /************************************************************************/
   10252             : /*                        GDALExtendedDataType()                        */
   10253             : /************************************************************************/
   10254             : 
   10255             : /** Copy constructor. */
   10256       17277 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
   10257       34554 :     : m_osName(other.m_osName), m_eClass(other.m_eClass),
   10258       17277 :       m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
   10259       17277 :       m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength),
   10260       17277 :       m_poRAT(other.m_poRAT ? other.m_poRAT->Clone() : nullptr)
   10261             : {
   10262       17277 :     if (m_eClass == GEDTC_COMPOUND)
   10263             :     {
   10264         521 :         for (const auto &elt : other.m_aoComponents)
   10265             :         {
   10266         341 :             m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
   10267             :         }
   10268             :     }
   10269       17277 : }
   10270             : 
   10271             : /************************************************************************/
   10272             : /*                            operator= ()                              */
   10273             : /************************************************************************/
   10274             : 
   10275             : /** Copy assignment. */
   10276             : GDALExtendedDataType &
   10277        1045 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
   10278             : {
   10279        1045 :     if (this != &other)
   10280             :     {
   10281        1045 :         m_osName = other.m_osName;
   10282        1045 :         m_eClass = other.m_eClass;
   10283        1045 :         m_eSubType = other.m_eSubType;
   10284        1045 :         m_eNumericDT = other.m_eNumericDT;
   10285        1045 :         m_nSize = other.m_nSize;
   10286        1045 :         m_nMaxStringLength = other.m_nMaxStringLength;
   10287        1045 :         m_poRAT.reset(other.m_poRAT ? other.m_poRAT->Clone() : nullptr);
   10288        1045 :         m_aoComponents.clear();
   10289        1045 :         if (m_eClass == GEDTC_COMPOUND)
   10290             :         {
   10291           0 :             for (const auto &elt : other.m_aoComponents)
   10292             :             {
   10293           0 :                 m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
   10294             :             }
   10295             :         }
   10296             :     }
   10297        1045 :     return *this;
   10298             : }
   10299             : 
   10300             : /************************************************************************/
   10301             : /*                            operator= ()                              */
   10302             : /************************************************************************/
   10303             : 
   10304             : /** Move assignment. */
   10305             : GDALExtendedDataType &
   10306       15239 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other)
   10307             : {
   10308       15239 :     m_osName = std::move(other.m_osName);
   10309       15239 :     m_eClass = other.m_eClass;
   10310       15239 :     m_eSubType = other.m_eSubType;
   10311       15239 :     m_eNumericDT = other.m_eNumericDT;
   10312       15239 :     m_nSize = other.m_nSize;
   10313       15239 :     m_nMaxStringLength = other.m_nMaxStringLength;
   10314       15239 :     m_aoComponents = std::move(other.m_aoComponents);
   10315       15239 :     m_poRAT = std::move(other.m_poRAT);
   10316       15239 :     other.m_eClass = GEDTC_NUMERIC;
   10317       15239 :     other.m_eNumericDT = GDT_Unknown;
   10318       15239 :     other.m_nSize = 0;
   10319       15239 :     other.m_nMaxStringLength = 0;
   10320       15239 :     return *this;
   10321             : }
   10322             : 
   10323             : /************************************************************************/
   10324             : /*                           Create()                                   */
   10325             : /************************************************************************/
   10326             : 
   10327             : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
   10328             :  *
   10329             :  * This is the same as the C function GDALExtendedDataTypeCreate()
   10330             :  *
   10331             :  * @param eType Numeric data type. Must be different from GDT_Unknown and
   10332             :  * GDT_TypeCount
   10333             :  */
   10334       37213 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
   10335             : {
   10336       37213 :     return GDALExtendedDataType(eType);
   10337             : }
   10338             : 
   10339             : /************************************************************************/
   10340             : /*                           Create()                                   */
   10341             : /************************************************************************/
   10342             : 
   10343             : /** Return a new GDALExtendedDataType from a raster attribute table.
   10344             :  *
   10345             :  * @param osName Type name
   10346             :  * @param eBaseType Base integer data type.
   10347             :  * @param poRAT Raster attribute table. Must not be NULL.
   10348             :  * @since 3.12
   10349             :  */
   10350             : GDALExtendedDataType
   10351          63 : GDALExtendedDataType::Create(const std::string &osName, GDALDataType eBaseType,
   10352             :                              std::unique_ptr<GDALRasterAttributeTable> poRAT)
   10353             : {
   10354          63 :     return GDALExtendedDataType(osName, eBaseType, std::move(poRAT));
   10355             : }
   10356             : 
   10357             : /************************************************************************/
   10358             : /*                           Create()                                   */
   10359             : /************************************************************************/
   10360             : 
   10361             : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
   10362             :  *
   10363             :  * This is the same as the C function GDALExtendedDataTypeCreateCompound()
   10364             :  *
   10365             :  * @param osName Type name.
   10366             :  * @param nTotalSize Total size of the type in bytes.
   10367             :  *                   Should be large enough to store all components.
   10368             :  * @param components Components of the compound type.
   10369             :  */
   10370         856 : GDALExtendedDataType GDALExtendedDataType::Create(
   10371             :     const std::string &osName, size_t nTotalSize,
   10372             :     std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
   10373             : {
   10374         856 :     size_t nLastOffset = 0;
   10375             :     // Some arbitrary threshold to avoid potential integer overflows
   10376         856 :     if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
   10377             :     {
   10378           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10379           2 :         return GDALExtendedDataType(GDT_Unknown);
   10380             :     }
   10381        4076 :     for (const auto &comp : components)
   10382             :     {
   10383             :         // Check alignment too ?
   10384        3223 :         if (comp->GetOffset() < nLastOffset)
   10385             :         {
   10386           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10387           1 :             return GDALExtendedDataType(GDT_Unknown);
   10388             :         }
   10389        3222 :         nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
   10390             :     }
   10391         853 :     if (nTotalSize < nLastOffset)
   10392             :     {
   10393           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10394           1 :         return GDALExtendedDataType(GDT_Unknown);
   10395             :     }
   10396         852 :     if (nTotalSize == 0 || components.empty())
   10397             :     {
   10398           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
   10399           3 :         return GDALExtendedDataType(GDT_Unknown);
   10400             :     }
   10401         849 :     return GDALExtendedDataType(osName, nTotalSize, std::move(components));
   10402             : }
   10403             : 
   10404             : /************************************************************************/
   10405             : /*                           Create()                                   */
   10406             : /************************************************************************/
   10407             : 
   10408             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   10409             :  *
   10410             :  * This is the same as the C function GDALExtendedDataTypeCreateString().
   10411             :  *
   10412             :  * @param nMaxStringLength maximum length of a string in bytes. 0 if
   10413             :  * unknown/unlimited
   10414             :  * @param eSubType Subtype.
   10415             :  */
   10416             : GDALExtendedDataType
   10417        9520 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
   10418             :                                    GDALExtendedDataTypeSubType eSubType)
   10419             : {
   10420        9520 :     return GDALExtendedDataType(nMaxStringLength, eSubType);
   10421             : }
   10422             : 
   10423             : /************************************************************************/
   10424             : /*                           operator==()                               */
   10425             : /************************************************************************/
   10426             : 
   10427             : /** Equality operator.
   10428             :  *
   10429             :  * This is the same as the C function GDALExtendedDataTypeEquals().
   10430             :  */
   10431        2714 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
   10432             : {
   10433        2687 :     if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
   10434        5401 :         m_nSize != other.m_nSize || m_osName != other.m_osName)
   10435             :     {
   10436         254 :         return false;
   10437             :     }
   10438        2460 :     if (m_eClass == GEDTC_NUMERIC)
   10439             :     {
   10440         873 :         return m_eNumericDT == other.m_eNumericDT;
   10441             :     }
   10442        1587 :     if (m_eClass == GEDTC_STRING)
   10443             :     {
   10444        1369 :         return true;
   10445             :     }
   10446         218 :     CPLAssert(m_eClass == GEDTC_COMPOUND);
   10447         218 :     if (m_aoComponents.size() != other.m_aoComponents.size())
   10448             :     {
   10449           2 :         return false;
   10450             :     }
   10451        1139 :     for (size_t i = 0; i < m_aoComponents.size(); i++)
   10452             :     {
   10453         923 :         if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
   10454             :         {
   10455           0 :             return false;
   10456             :         }
   10457             :     }
   10458         216 :     return true;
   10459             : }
   10460             : 
   10461             : /************************************************************************/
   10462             : /*                        CanConvertTo()                                */
   10463             : /************************************************************************/
   10464             : 
   10465             : /** Return whether this data type can be converted to the other one.
   10466             :  *
   10467             :  * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
   10468             :  *
   10469             :  * @param other Target data type for the conversion being considered.
   10470             :  */
   10471        8489 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
   10472             : {
   10473        8489 :     if (m_eClass == GEDTC_NUMERIC)
   10474             :     {
   10475        5926 :         if (m_eNumericDT == GDT_Unknown)
   10476           0 :             return false;
   10477        5926 :         if (other.m_eClass == GEDTC_NUMERIC &&
   10478        5823 :             other.m_eNumericDT == GDT_Unknown)
   10479           0 :             return false;
   10480        6029 :         return other.m_eClass == GEDTC_NUMERIC ||
   10481        6029 :                other.m_eClass == GEDTC_STRING;
   10482             :     }
   10483        2563 :     if (m_eClass == GEDTC_STRING)
   10484             :     {
   10485        2385 :         return other.m_eClass == m_eClass;
   10486             :     }
   10487         178 :     CPLAssert(m_eClass == GEDTC_COMPOUND);
   10488         178 :     if (other.m_eClass != GEDTC_COMPOUND)
   10489           0 :         return false;
   10490             :     std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
   10491         356 :         srcComponents;
   10492         905 :     for (const auto &srcComp : m_aoComponents)
   10493             :     {
   10494         727 :         srcComponents[srcComp->GetName()] = &srcComp;
   10495             :     }
   10496         497 :     for (const auto &dstComp : other.m_aoComponents)
   10497             :     {
   10498         320 :         auto oIter = srcComponents.find(dstComp->GetName());
   10499         320 :         if (oIter == srcComponents.end())
   10500           1 :             return false;
   10501         319 :         if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
   10502           0 :             return false;
   10503             :     }
   10504         177 :     return true;
   10505             : }
   10506             : 
   10507             : /************************************************************************/
   10508             : /*                     NeedsFreeDynamicMemory()                         */
   10509             : /************************************************************************/
   10510             : 
   10511             : /** Return whether the data type holds dynamically allocated memory, that
   10512             :  * needs to be freed with FreeDynamicMemory().
   10513             :  *
   10514             :  */
   10515        3726 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
   10516             : {
   10517        3726 :     switch (m_eClass)
   10518             :     {
   10519         934 :         case GEDTC_STRING:
   10520         934 :             return true;
   10521             : 
   10522        2682 :         case GEDTC_NUMERIC:
   10523        2682 :             return false;
   10524             : 
   10525         110 :         case GEDTC_COMPOUND:
   10526             :         {
   10527         223 :             for (const auto &comp : m_aoComponents)
   10528             :             {
   10529         209 :                 if (comp->GetType().NeedsFreeDynamicMemory())
   10530          96 :                     return true;
   10531             :             }
   10532             :         }
   10533             :     }
   10534          14 :     return false;
   10535             : }
   10536             : 
   10537             : /************************************************************************/
   10538             : /*                        FreeDynamicMemory()                           */
   10539             : /************************************************************************/
   10540             : 
   10541             : /** Release the dynamic memory (strings typically) from a raw value.
   10542             :  *
   10543             :  * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
   10544             :  *
   10545             :  * @param pBuffer Raw buffer of a single element of an attribute or array value.
   10546             :  */
   10547        3859 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
   10548             : {
   10549        3859 :     switch (m_eClass)
   10550             :     {
   10551        2784 :         case GEDTC_STRING:
   10552             :         {
   10553             :             char *pszStr;
   10554        2784 :             memcpy(&pszStr, pBuffer, sizeof(char *));
   10555        2784 :             if (pszStr)
   10556             :             {
   10557        2241 :                 VSIFree(pszStr);
   10558             :             }
   10559        2784 :             break;
   10560             :         }
   10561             : 
   10562         899 :         case GEDTC_NUMERIC:
   10563             :         {
   10564         899 :             break;
   10565             :         }
   10566             : 
   10567         176 :         case GEDTC_COMPOUND:
   10568             :         {
   10569         176 :             GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
   10570         938 :             for (const auto &comp : m_aoComponents)
   10571             :             {
   10572        1524 :                 comp->GetType().FreeDynamicMemory(pabyBuffer +
   10573         762 :                                                   comp->GetOffset());
   10574             :             }
   10575         176 :             break;
   10576             :         }
   10577             :     }
   10578        3859 : }
   10579             : 
   10580             : /************************************************************************/
   10581             : /*                      ~GDALEDTComponent()                             */
   10582             : /************************************************************************/
   10583             : 
   10584             : GDALEDTComponent::~GDALEDTComponent() = default;
   10585             : 
   10586             : /************************************************************************/
   10587             : /*                      GDALEDTComponent()                              */
   10588             : /************************************************************************/
   10589             : 
   10590             : /** constructor of a GDALEDTComponent
   10591             :  *
   10592             :  * This is the same as the C function GDALEDTComponendCreate()
   10593             :  *
   10594             :  * @param name Component name
   10595             :  * @param offset Offset in byte of the component in the compound data type.
   10596             :  *               In case of nesting of compound data type, this should be
   10597             :  *               the offset to the immediate belonging data type, not to the
   10598             :  *               higher level one.
   10599             :  * @param type   Component data type.
   10600             :  */
   10601        3214 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
   10602        3214 :                                    const GDALExtendedDataType &type)
   10603        3214 :     : m_osName(name), m_nOffset(offset), m_oType(type)
   10604             : {
   10605        3214 : }
   10606             : 
   10607             : /************************************************************************/
   10608             : /*                      GDALEDTComponent()                              */
   10609             : /************************************************************************/
   10610             : 
   10611             : /** Copy constructor. */
   10612             : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
   10613             : 
   10614             : /************************************************************************/
   10615             : /*                           operator==()                               */
   10616             : /************************************************************************/
   10617             : 
   10618             : /** Equality operator.
   10619             :  */
   10620         923 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
   10621             : {
   10622        1846 :     return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
   10623        1846 :            m_oType == other.m_oType;
   10624             : }
   10625             : 
   10626             : /************************************************************************/
   10627             : /*                        ~GDALDimension()                              */
   10628             : /************************************************************************/
   10629             : 
   10630             : GDALDimension::~GDALDimension() = default;
   10631             : 
   10632             : /************************************************************************/
   10633             : /*                         GDALDimension()                              */
   10634             : /************************************************************************/
   10635             : 
   10636             : //! @cond Doxygen_Suppress
   10637             : /** Constructor.
   10638             :  *
   10639             :  * @param osParentName Parent name
   10640             :  * @param osName name
   10641             :  * @param osType type. See GetType().
   10642             :  * @param osDirection direction. See GetDirection().
   10643             :  * @param nSize size.
   10644             :  */
   10645        8486 : GDALDimension::GDALDimension(const std::string &osParentName,
   10646             :                              const std::string &osName,
   10647             :                              const std::string &osType,
   10648        8486 :                              const std::string &osDirection, GUInt64 nSize)
   10649             :     : m_osName(osName),
   10650             :       m_osFullName(
   10651        8486 :           !osParentName.empty()
   10652       12503 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
   10653             :               : osName),
   10654       29475 :       m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
   10655             : {
   10656        8486 : }
   10657             : 
   10658             : //! @endcond
   10659             : 
   10660             : /************************************************************************/
   10661             : /*                         GetIndexingVariable()                        */
   10662             : /************************************************************************/
   10663             : 
   10664             : /** Return the variable that is used to index the dimension (if there is one).
   10665             :  *
   10666             :  * This is the array, typically one-dimensional, describing the values taken
   10667             :  * by the dimension.
   10668             :  */
   10669          73 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
   10670             : {
   10671          73 :     return nullptr;
   10672             : }
   10673             : 
   10674             : /************************************************************************/
   10675             : /*                         SetIndexingVariable()                        */
   10676             : /************************************************************************/
   10677             : 
   10678             : /** Set the variable that is used to index the dimension.
   10679             :  *
   10680             :  * This is the array, typically one-dimensional, describing the values taken
   10681             :  * by the dimension.
   10682             :  *
   10683             :  * Optionally implemented by drivers.
   10684             :  *
   10685             :  * Drivers known to implement it: MEM.
   10686             :  *
   10687             :  * @param poArray Variable to use to index the dimension.
   10688             :  * @return true in case of success.
   10689             :  */
   10690           3 : bool GDALDimension::SetIndexingVariable(
   10691             :     CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
   10692             : {
   10693           3 :     CPLError(CE_Failure, CPLE_NotSupported,
   10694             :              "SetIndexingVariable() not implemented");
   10695           3 :     return false;
   10696             : }
   10697             : 
   10698             : /************************************************************************/
   10699             : /*                            Rename()                                  */
   10700             : /************************************************************************/
   10701             : 
   10702             : /** Rename the dimension.
   10703             :  *
   10704             :  * This is not implemented by all drivers.
   10705             :  *
   10706             :  * Drivers known to implement it: MEM, netCDF, ZARR.
   10707             :  *
   10708             :  * This is the same as the C function GDALDimensionRename().
   10709             :  *
   10710             :  * @param osNewName New name.
   10711             :  *
   10712             :  * @return true in case of success
   10713             :  * @since GDAL 3.8
   10714             :  */
   10715           0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
   10716             : {
   10717           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
   10718           0 :     return false;
   10719             : }
   10720             : 
   10721             : /************************************************************************/
   10722             : /*                         BaseRename()                                 */
   10723             : /************************************************************************/
   10724             : 
   10725             : //! @cond Doxygen_Suppress
   10726           8 : void GDALDimension::BaseRename(const std::string &osNewName)
   10727             : {
   10728           8 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
   10729           8 :     m_osFullName += osNewName;
   10730           8 :     m_osName = osNewName;
   10731           8 : }
   10732             : 
   10733             : //! @endcond
   10734             : 
   10735             : //! @cond Doxygen_Suppress
   10736             : /************************************************************************/
   10737             : /*                          ParentRenamed()                             */
   10738             : /************************************************************************/
   10739             : 
   10740           8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
   10741             : {
   10742           8 :     m_osFullName = osNewParentFullName;
   10743           8 :     m_osFullName += "/";
   10744           8 :     m_osFullName += m_osName;
   10745           8 : }
   10746             : 
   10747             : //! @endcond
   10748             : 
   10749             : //! @cond Doxygen_Suppress
   10750             : /************************************************************************/
   10751             : /*                          ParentDeleted()                             */
   10752             : /************************************************************************/
   10753             : 
   10754           4 : void GDALDimension::ParentDeleted()
   10755             : {
   10756           4 : }
   10757             : 
   10758             : //! @endcond
   10759             : 
   10760             : /************************************************************************/
   10761             : /************************************************************************/
   10762             : /************************************************************************/
   10763             : /*                              C API                                   */
   10764             : /************************************************************************/
   10765             : /************************************************************************/
   10766             : /************************************************************************/
   10767             : 
   10768             : /************************************************************************/
   10769             : /*                      GDALExtendedDataTypeCreate()                    */
   10770             : /************************************************************************/
   10771             : 
   10772             : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
   10773             :  *
   10774             :  * This is the same as the C++ method GDALExtendedDataType::Create()
   10775             :  *
   10776             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10777             :  *
   10778             :  * @param eType Numeric data type. Must be different from GDT_Unknown and
   10779             :  * GDT_TypeCount
   10780             :  *
   10781             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10782             :  */
   10783        1996 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
   10784             : {
   10785        1996 :     if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
   10786             :     {
   10787           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
   10788             :                  "Illegal GDT_Unknown/GDT_TypeCount argument");
   10789           0 :         return nullptr;
   10790             :     }
   10791             :     return new GDALExtendedDataTypeHS(
   10792        1996 :         new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
   10793             : }
   10794             : 
   10795             : /************************************************************************/
   10796             : /*                    GDALExtendedDataTypeCreateString()                */
   10797             : /************************************************************************/
   10798             : 
   10799             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   10800             :  *
   10801             :  * This is the same as the C++ method GDALExtendedDataType::CreateString()
   10802             :  *
   10803             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10804             :  *
   10805             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10806             :  */
   10807           0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
   10808             : {
   10809           0 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   10810           0 :         GDALExtendedDataType::CreateString(nMaxStringLength)));
   10811             : }
   10812             : 
   10813             : /************************************************************************/
   10814             : /*                   GDALExtendedDataTypeCreateStringEx()               */
   10815             : /************************************************************************/
   10816             : 
   10817             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   10818             :  *
   10819             :  * This is the same as the C++ method GDALExtendedDataType::CreateString()
   10820             :  *
   10821             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10822             :  *
   10823             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10824             :  * @since GDAL 3.4
   10825             :  */
   10826             : GDALExtendedDataTypeH
   10827         188 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
   10828             :                                    GDALExtendedDataTypeSubType eSubType)
   10829             : {
   10830         188 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   10831         188 :         GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
   10832             : }
   10833             : 
   10834             : /************************************************************************/
   10835             : /*                   GDALExtendedDataTypeCreateCompound()               */
   10836             : /************************************************************************/
   10837             : 
   10838             : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
   10839             :  *
   10840             :  * This is the same as the C++ method GDALExtendedDataType::Create(const
   10841             :  * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
   10842             :  *
   10843             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10844             :  *
   10845             :  * @param pszName Type name.
   10846             :  * @param nTotalSize Total size of the type in bytes.
   10847             :  *                   Should be large enough to store all components.
   10848             :  * @param nComponents Number of components in comps array.
   10849             :  * @param comps Components.
   10850             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10851             :  */
   10852             : GDALExtendedDataTypeH
   10853          22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
   10854             :                                    size_t nComponents,
   10855             :                                    const GDALEDTComponentH *comps)
   10856             : {
   10857          44 :     std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
   10858          54 :     for (size_t i = 0; i < nComponents; i++)
   10859             :     {
   10860          64 :         compsCpp.emplace_back(std::unique_ptr<GDALEDTComponent>(
   10861          64 :             new GDALEDTComponent(*(comps[i]->m_poImpl.get()))));
   10862             :     }
   10863             :     auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
   10864          66 :                                            std::move(compsCpp));
   10865          22 :     if (dt.GetClass() != GEDTC_COMPOUND)
   10866           6 :         return nullptr;
   10867          16 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(dt));
   10868             : }
   10869             : 
   10870             : /************************************************************************/
   10871             : /*                     GDALExtendedDataTypeRelease()                    */
   10872             : /************************************************************************/
   10873             : 
   10874             : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
   10875             :  *
   10876             :  * Note: when applied on a object coming from a driver, this does not
   10877             :  * destroy the object in the file, database, etc...
   10878             :  */
   10879        6604 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
   10880             : {
   10881        6604 :     delete hEDT;
   10882        6604 : }
   10883             : 
   10884             : /************************************************************************/
   10885             : /*                     GDALExtendedDataTypeGetName()                    */
   10886             : /************************************************************************/
   10887             : 
   10888             : /** Return type name.
   10889             :  *
   10890             :  * This is the same as the C++ method GDALExtendedDataType::GetName()
   10891             :  */
   10892           8 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
   10893             : {
   10894           8 :     VALIDATE_POINTER1(hEDT, __func__, "");
   10895           8 :     return hEDT->m_poImpl->GetName().c_str();
   10896             : }
   10897             : 
   10898             : /************************************************************************/
   10899             : /*                     GDALExtendedDataTypeGetClass()                    */
   10900             : /************************************************************************/
   10901             : 
   10902             : /** Return type class.
   10903             :  *
   10904             :  * This is the same as the C++ method GDALExtendedDataType::GetClass()
   10905             :  */
   10906             : GDALExtendedDataTypeClass
   10907        9175 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
   10908             : {
   10909        9175 :     VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
   10910        9175 :     return hEDT->m_poImpl->GetClass();
   10911             : }
   10912             : 
   10913             : /************************************************************************/
   10914             : /*               GDALExtendedDataTypeGetNumericDataType()               */
   10915             : /************************************************************************/
   10916             : 
   10917             : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
   10918             :  *
   10919             :  * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
   10920             :  */
   10921        2069 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
   10922             : {
   10923        2069 :     VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
   10924        2069 :     return hEDT->m_poImpl->GetNumericDataType();
   10925             : }
   10926             : 
   10927             : /************************************************************************/
   10928             : /*                   GDALExtendedDataTypeGetSize()                      */
   10929             : /************************************************************************/
   10930             : 
   10931             : /** Return data type size in bytes.
   10932             :  *
   10933             :  * This is the same as the C++ method GDALExtendedDataType::GetSize()
   10934             :  */
   10935        2538 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
   10936             : {
   10937        2538 :     VALIDATE_POINTER1(hEDT, __func__, 0);
   10938        2538 :     return hEDT->m_poImpl->GetSize();
   10939             : }
   10940             : 
   10941             : /************************************************************************/
   10942             : /*              GDALExtendedDataTypeGetMaxStringLength()                */
   10943             : /************************************************************************/
   10944             : 
   10945             : /** Return the maximum length of a string in bytes.
   10946             :  *
   10947             :  * 0 indicates unknown/unlimited string.
   10948             :  *
   10949             :  * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
   10950             :  */
   10951           3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
   10952             : {
   10953           3 :     VALIDATE_POINTER1(hEDT, __func__, 0);
   10954           3 :     return hEDT->m_poImpl->GetMaxStringLength();
   10955             : }
   10956             : 
   10957             : /************************************************************************/
   10958             : /*                    GDALExtendedDataTypeCanConvertTo()                */
   10959             : /************************************************************************/
   10960             : 
   10961             : /** Return whether this data type can be converted to the other one.
   10962             :  *
   10963             :  * This is the same as the C function GDALExtendedDataType::CanConvertTo()
   10964             :  *
   10965             :  * @param hSourceEDT Source data type for the conversion being considered.
   10966             :  * @param hTargetEDT Target data type for the conversion being considered.
   10967             :  * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
   10968             :  */
   10969           7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
   10970             :                                      GDALExtendedDataTypeH hTargetEDT)
   10971             : {
   10972           7 :     VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
   10973           7 :     VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
   10974           7 :     return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
   10975             : }
   10976             : 
   10977             : /************************************************************************/
   10978             : /*                        GDALExtendedDataTypeEquals()                  */
   10979             : /************************************************************************/
   10980             : 
   10981             : /** Return whether this data type is equal to another one.
   10982             :  *
   10983             :  * This is the same as the C++ method GDALExtendedDataType::operator==()
   10984             :  *
   10985             :  * @param hFirstEDT First data type.
   10986             :  * @param hSecondEDT Second data type.
   10987             :  * @return TRUE if they are equal. FALSE otherwise.
   10988             :  */
   10989         100 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
   10990             :                                GDALExtendedDataTypeH hSecondEDT)
   10991             : {
   10992         100 :     VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
   10993         100 :     VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
   10994         100 :     return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
   10995             : }
   10996             : 
   10997             : /************************************************************************/
   10998             : /*                    GDALExtendedDataTypeGetSubType()                  */
   10999             : /************************************************************************/
   11000             : 
   11001             : /** Return the subtype of a type.
   11002             :  *
   11003             :  * This is the same as the C++ method GDALExtendedDataType::GetSubType()
   11004             :  *
   11005             :  * @param hEDT Data type.
   11006             :  * @return subtype.
   11007             :  * @since 3.4
   11008             :  */
   11009             : GDALExtendedDataTypeSubType
   11010         104 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
   11011             : {
   11012         104 :     VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
   11013         104 :     return hEDT->m_poImpl->GetSubType();
   11014             : }
   11015             : 
   11016             : /************************************************************************/
   11017             : /*                      GDALExtendedDataTypeGetRAT()                    */
   11018             : /************************************************************************/
   11019             : 
   11020             : /** Return associated raster attribute table, when there is one.
   11021             :  *
   11022             :  * * For the netCDF driver, the RAT will capture enumerated types, with
   11023             :  * a "value" column with an integer value and a "name" column with the
   11024             :  * associated name.
   11025             :  * This is the same as the C++ method GDALExtendedDataType::GetRAT()
   11026             :  *
   11027             :  * @param hEDT Data type.
   11028             :  * @return raster attribute (owned by GDALExtendedDataTypeH), or NULL
   11029             :  * @since 3.12
   11030             :  */
   11031           1 : GDALRasterAttributeTableH GDALExtendedDataTypeGetRAT(GDALExtendedDataTypeH hEDT)
   11032             : {
   11033           1 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11034           1 :     return GDALRasterAttributeTable::ToHandle(
   11035           2 :         const_cast<GDALRasterAttributeTable *>(hEDT->m_poImpl->GetRAT()));
   11036             : }
   11037             : 
   11038             : /************************************************************************/
   11039             : /*                     GDALExtendedDataTypeGetComponents()              */
   11040             : /************************************************************************/
   11041             : 
   11042             : /** Return the components of the data type (only valid when GetClass() ==
   11043             :  * GEDTC_COMPOUND)
   11044             :  *
   11045             :  * The returned array and its content must be freed with
   11046             :  * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
   11047             :  * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
   11048             :  * individual array members).
   11049             :  *
   11050             :  * This is the same as the C++ method GDALExtendedDataType::GetComponents()
   11051             :  *
   11052             :  * @param hEDT Data type
   11053             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11054             :  * @return an array of *pnCount components.
   11055             :  */
   11056          44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
   11057             :                                                      size_t *pnCount)
   11058             : {
   11059          44 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11060          44 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11061          44 :     const auto &components = hEDT->m_poImpl->GetComponents();
   11062             :     auto ret = static_cast<GDALEDTComponentH *>(
   11063          44 :         CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
   11064         131 :     for (size_t i = 0; i < components.size(); i++)
   11065             :     {
   11066          87 :         ret[i] = new GDALEDTComponentHS(*components[i].get());
   11067             :     }
   11068          44 :     *pnCount = components.size();
   11069          44 :     return ret;
   11070             : }
   11071             : 
   11072             : /************************************************************************/
   11073             : /*                     GDALExtendedDataTypeFreeComponents()             */
   11074             : /************************************************************************/
   11075             : 
   11076             : /** Free the return of GDALExtendedDataTypeGetComponents().
   11077             :  *
   11078             :  * @param components return value of GDALExtendedDataTypeGetComponents()
   11079             :  * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
   11080             :  */
   11081          44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
   11082             :                                         size_t nCount)
   11083             : {
   11084         131 :     for (size_t i = 0; i < nCount; i++)
   11085             :     {
   11086          87 :         delete components[i];
   11087             :     }
   11088          44 :     CPLFree(components);
   11089          44 : }
   11090             : 
   11091             : /************************************************************************/
   11092             : /*                         GDALEDTComponentCreate()                     */
   11093             : /************************************************************************/
   11094             : 
   11095             : /** Create a new GDALEDTComponent.
   11096             :  *
   11097             :  * The returned value must be freed with GDALEDTComponentRelease().
   11098             :  *
   11099             :  * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
   11100             :  */
   11101          20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
   11102             :                                          GDALExtendedDataTypeH hType)
   11103             : {
   11104          20 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11105          20 :     VALIDATE_POINTER1(hType, __func__, nullptr);
   11106             :     return new GDALEDTComponentHS(
   11107          20 :         GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
   11108             : }
   11109             : 
   11110             : /************************************************************************/
   11111             : /*                         GDALEDTComponentRelease()                    */
   11112             : /************************************************************************/
   11113             : 
   11114             : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
   11115             :  *
   11116             :  * Note: when applied on a object coming from a driver, this does not
   11117             :  * destroy the object in the file, database, etc...
   11118             :  */
   11119          61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
   11120             : {
   11121          61 :     delete hComp;
   11122          61 : }
   11123             : 
   11124             : /************************************************************************/
   11125             : /*                         GDALEDTComponentGetName()                    */
   11126             : /************************************************************************/
   11127             : 
   11128             : /** Return the name.
   11129             :  *
   11130             :  * The returned pointer is valid until hComp is released.
   11131             :  *
   11132             :  * This is the same as the C++ method GDALEDTComponent::GetName().
   11133             :  */
   11134          33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
   11135             : {
   11136          33 :     VALIDATE_POINTER1(hComp, __func__, nullptr);
   11137          33 :     return hComp->m_poImpl->GetName().c_str();
   11138             : }
   11139             : 
   11140             : /************************************************************************/
   11141             : /*                       GDALEDTComponentGetOffset()                    */
   11142             : /************************************************************************/
   11143             : 
   11144             : /** Return the offset (in bytes) of the component in the compound data type.
   11145             :  *
   11146             :  * This is the same as the C++ method GDALEDTComponent::GetOffset().
   11147             :  */
   11148          31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
   11149             : {
   11150          31 :     VALIDATE_POINTER1(hComp, __func__, 0);
   11151          31 :     return hComp->m_poImpl->GetOffset();
   11152             : }
   11153             : 
   11154             : /************************************************************************/
   11155             : /*                       GDALEDTComponentGetType()                      */
   11156             : /************************************************************************/
   11157             : 
   11158             : /** Return the data type of the component.
   11159             :  *
   11160             :  * This is the same as the C++ method GDALEDTComponent::GetType().
   11161             :  */
   11162          93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
   11163             : {
   11164          93 :     VALIDATE_POINTER1(hComp, __func__, nullptr);
   11165             :     return new GDALExtendedDataTypeHS(
   11166          93 :         new GDALExtendedDataType(hComp->m_poImpl->GetType()));
   11167             : }
   11168             : 
   11169             : /************************************************************************/
   11170             : /*                           GDALGroupRelease()                         */
   11171             : /************************************************************************/
   11172             : 
   11173             : /** Release the GDAL in-memory object associated with a GDALGroupH.
   11174             :  *
   11175             :  * Note: when applied on a object coming from a driver, this does not
   11176             :  * destroy the object in the file, database, etc...
   11177             :  */
   11178        1439 : void GDALGroupRelease(GDALGroupH hGroup)
   11179             : {
   11180        1439 :     delete hGroup;
   11181        1439 : }
   11182             : 
   11183             : /************************************************************************/
   11184             : /*                           GDALGroupGetName()                         */
   11185             : /************************************************************************/
   11186             : 
   11187             : /** Return the name of the group.
   11188             :  *
   11189             :  * The returned pointer is valid until hGroup is released.
   11190             :  *
   11191             :  * This is the same as the C++ method GDALGroup::GetName().
   11192             :  */
   11193          95 : const char *GDALGroupGetName(GDALGroupH hGroup)
   11194             : {
   11195          95 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11196          95 :     return hGroup->m_poImpl->GetName().c_str();
   11197             : }
   11198             : 
   11199             : /************************************************************************/
   11200             : /*                         GDALGroupGetFullName()                       */
   11201             : /************************************************************************/
   11202             : 
   11203             : /** Return the full name of the group.
   11204             :  *
   11205             :  * The returned pointer is valid until hGroup is released.
   11206             :  *
   11207             :  * This is the same as the C++ method GDALGroup::GetFullName().
   11208             :  */
   11209          47 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
   11210             : {
   11211          47 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11212          47 :     return hGroup->m_poImpl->GetFullName().c_str();
   11213             : }
   11214             : 
   11215             : /************************************************************************/
   11216             : /*                          GDALGroupGetMDArrayNames()                  */
   11217             : /************************************************************************/
   11218             : 
   11219             : /** Return the list of multidimensional array names contained in this group.
   11220             :  *
   11221             :  * This is the same as the C++ method GDALGroup::GetGroupNames().
   11222             :  *
   11223             :  * @return the array names, to be freed with CSLDestroy()
   11224             :  */
   11225         329 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
   11226             : {
   11227         329 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11228         658 :     auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
   11229         658 :     CPLStringList res;
   11230         829 :     for (const auto &name : names)
   11231             :     {
   11232         500 :         res.AddString(name.c_str());
   11233             :     }
   11234         329 :     return res.StealList();
   11235             : }
   11236             : 
   11237             : /************************************************************************/
   11238             : /*                  GDALGroupGetMDArrayFullNamesRecursive()             */
   11239             : /************************************************************************/
   11240             : 
   11241             : /** Return the list of multidimensional array full names contained in this
   11242             :  * group and its subgroups.
   11243             :  *
   11244             :  * This is the same as the C++ method GDALGroup::GetMDArrayFullNamesRecursive().
   11245             :  *
   11246             :  * @return the array names, to be freed with CSLDestroy()
   11247             :  *
   11248             :  * @since 3.11
   11249             :  */
   11250           1 : char **GDALGroupGetMDArrayFullNamesRecursive(GDALGroupH hGroup,
   11251             :                                              CSLConstList papszGroupOptions,
   11252             :                                              CSLConstList papszArrayOptions)
   11253             : {
   11254           1 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11255           1 :     auto names = hGroup->m_poImpl->GetMDArrayFullNamesRecursive(
   11256           2 :         papszGroupOptions, papszArrayOptions);
   11257           2 :     CPLStringList res;
   11258           5 :     for (const auto &name : names)
   11259             :     {
   11260           4 :         res.AddString(name.c_str());
   11261             :     }
   11262           1 :     return res.StealList();
   11263             : }
   11264             : 
   11265             : /************************************************************************/
   11266             : /*                          GDALGroupOpenMDArray()                      */
   11267             : /************************************************************************/
   11268             : 
   11269             : /** Open and return a multidimensional array.
   11270             :  *
   11271             :  * This is the same as the C++ method GDALGroup::OpenMDArray().
   11272             :  *
   11273             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11274             :  */
   11275         806 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
   11276             :                                   CSLConstList papszOptions)
   11277             : {
   11278         806 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11279         806 :     VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
   11280        2418 :     auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
   11281        2418 :                                                papszOptions);
   11282         806 :     if (!array)
   11283          30 :         return nullptr;
   11284         776 :     return new GDALMDArrayHS(array);
   11285             : }
   11286             : 
   11287             : /************************************************************************/
   11288             : /*                  GDALGroupOpenMDArrayFromFullname()                  */
   11289             : /************************************************************************/
   11290             : 
   11291             : /** Open and return a multidimensional array from its fully qualified name.
   11292             :  *
   11293             :  * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
   11294             :  *
   11295             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11296             :  *
   11297             :  * @since GDAL 3.2
   11298             :  */
   11299          16 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
   11300             :                                               const char *pszFullname,
   11301             :                                               CSLConstList papszOptions)
   11302             : {
   11303          16 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11304          16 :     VALIDATE_POINTER1(pszFullname, __func__, nullptr);
   11305          16 :     auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
   11306          48 :         std::string(pszFullname), papszOptions);
   11307          16 :     if (!array)
   11308           2 :         return nullptr;
   11309          14 :     return new GDALMDArrayHS(array);
   11310             : }
   11311             : 
   11312             : /************************************************************************/
   11313             : /*                      GDALGroupResolveMDArray()                       */
   11314             : /************************************************************************/
   11315             : 
   11316             : /** Locate an array in a group and its subgroups by name.
   11317             :  *
   11318             :  * See GDALGroup::ResolveMDArray() for description of the behavior.
   11319             :  * @since GDAL 3.2
   11320             :  */
   11321          19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
   11322             :                                      const char *pszStartingPoint,
   11323             :                                      CSLConstList papszOptions)
   11324             : {
   11325          19 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11326          19 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11327          19 :     VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
   11328          19 :     auto array = hGroup->m_poImpl->ResolveMDArray(
   11329          57 :         std::string(pszName), std::string(pszStartingPoint), papszOptions);
   11330          19 :     if (!array)
   11331           2 :         return nullptr;
   11332          17 :     return new GDALMDArrayHS(array);
   11333             : }
   11334             : 
   11335             : /************************************************************************/
   11336             : /*                        GDALGroupGetGroupNames()                      */
   11337             : /************************************************************************/
   11338             : 
   11339             : /** Return the list of sub-groups contained in this group.
   11340             :  *
   11341             :  * This is the same as the C++ method GDALGroup::GetGroupNames().
   11342             :  *
   11343             :  * @return the group names, to be freed with CSLDestroy()
   11344             :  */
   11345          97 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
   11346             : {
   11347          97 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11348         194 :     auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
   11349         194 :     CPLStringList res;
   11350         219 :     for (const auto &name : names)
   11351             :     {
   11352         122 :         res.AddString(name.c_str());
   11353             :     }
   11354          97 :     return res.StealList();
   11355             : }
   11356             : 
   11357             : /************************************************************************/
   11358             : /*                           GDALGroupOpenGroup()                       */
   11359             : /************************************************************************/
   11360             : 
   11361             : /** Open and return a sub-group.
   11362             :  *
   11363             :  * This is the same as the C++ method GDALGroup::OpenGroup().
   11364             :  *
   11365             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11366             :  */
   11367         163 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11368             :                               CSLConstList papszOptions)
   11369             : {
   11370         163 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11371         163 :     VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
   11372             :     auto subGroup =
   11373         489 :         hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
   11374         163 :     if (!subGroup)
   11375          30 :         return nullptr;
   11376         133 :     return new GDALGroupHS(subGroup);
   11377             : }
   11378             : 
   11379             : /************************************************************************/
   11380             : /*                   GDALGroupGetVectorLayerNames()                     */
   11381             : /************************************************************************/
   11382             : 
   11383             : /** Return the list of layer names contained in this group.
   11384             :  *
   11385             :  * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
   11386             :  *
   11387             :  * @return the group names, to be freed with CSLDestroy()
   11388             :  * @since 3.4
   11389             :  */
   11390           8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
   11391             :                                     CSLConstList papszOptions)
   11392             : {
   11393           8 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11394          16 :     auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
   11395          16 :     CPLStringList res;
   11396          18 :     for (const auto &name : names)
   11397             :     {
   11398          10 :         res.AddString(name.c_str());
   11399             :     }
   11400           8 :     return res.StealList();
   11401             : }
   11402             : 
   11403             : /************************************************************************/
   11404             : /*                      GDALGroupOpenVectorLayer()                      */
   11405             : /************************************************************************/
   11406             : 
   11407             : /** Open and return a vector layer.
   11408             :  *
   11409             :  * This is the same as the C++ method GDALGroup::OpenVectorLayer().
   11410             :  *
   11411             :  * Note that the vector layer is owned by its parent GDALDatasetH, and thus
   11412             :  * the returned handled if only valid while the parent GDALDatasetH is kept
   11413             :  * opened.
   11414             :  *
   11415             :  * @return the vector layer, or nullptr.
   11416             :  * @since 3.4
   11417             :  */
   11418          12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
   11419             :                                    const char *pszVectorLayerName,
   11420             :                                    CSLConstList papszOptions)
   11421             : {
   11422          12 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11423          12 :     VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
   11424          24 :     return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
   11425          24 :         std::string(pszVectorLayerName), papszOptions));
   11426             : }
   11427             : 
   11428             : /************************************************************************/
   11429             : /*                       GDALGroupOpenMDArrayFromFullname()             */
   11430             : /************************************************************************/
   11431             : 
   11432             : /** Open and return a sub-group from its fully qualified name.
   11433             :  *
   11434             :  * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
   11435             :  *
   11436             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11437             :  *
   11438             :  * @since GDAL 3.2
   11439             :  */
   11440           3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
   11441             :                                           const char *pszFullname,
   11442             :                                           CSLConstList papszOptions)
   11443             : {
   11444           3 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11445           3 :     VALIDATE_POINTER1(pszFullname, __func__, nullptr);
   11446           3 :     auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
   11447           9 :         std::string(pszFullname), papszOptions);
   11448           3 :     if (!subGroup)
   11449           2 :         return nullptr;
   11450           1 :     return new GDALGroupHS(subGroup);
   11451             : }
   11452             : 
   11453             : /************************************************************************/
   11454             : /*                         GDALGroupGetDimensions()                     */
   11455             : /************************************************************************/
   11456             : 
   11457             : /** Return the list of dimensions contained in this group and used by its
   11458             :  * arrays.
   11459             :  *
   11460             :  * The returned array must be freed with GDALReleaseDimensions().  If only the
   11461             :  * array itself needs to be freed, CPLFree() should be called (and
   11462             :  * GDALDimensionRelease() on individual array members).
   11463             :  *
   11464             :  * This is the same as the C++ method GDALGroup::GetDimensions().
   11465             :  *
   11466             :  * @param hGroup Group.
   11467             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11468             :  * @param papszOptions Driver specific options determining how dimensions
   11469             :  * should be retrieved. Pass nullptr for default behavior.
   11470             :  *
   11471             :  * @return an array of *pnCount dimensions.
   11472             :  */
   11473          73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
   11474             :                                        CSLConstList papszOptions)
   11475             : {
   11476          73 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11477          73 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11478          73 :     auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
   11479             :     auto ret = static_cast<GDALDimensionH *>(
   11480          73 :         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
   11481         230 :     for (size_t i = 0; i < dims.size(); i++)
   11482             :     {
   11483         157 :         ret[i] = new GDALDimensionHS(dims[i]);
   11484             :     }
   11485          73 :     *pnCount = dims.size();
   11486          73 :     return ret;
   11487             : }
   11488             : 
   11489             : /************************************************************************/
   11490             : /*                          GDALGroupGetAttribute()                     */
   11491             : /************************************************************************/
   11492             : 
   11493             : /** Return an attribute by its name.
   11494             :  *
   11495             :  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
   11496             :  *
   11497             :  * The returned attribute must be freed with GDALAttributeRelease().
   11498             :  */
   11499          80 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
   11500             : {
   11501          80 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11502          80 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11503         240 :     auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
   11504          80 :     if (attr)
   11505          76 :         return new GDALAttributeHS(attr);
   11506           4 :     return nullptr;
   11507             : }
   11508             : 
   11509             : /************************************************************************/
   11510             : /*                         GDALGroupGetAttributes()                     */
   11511             : /************************************************************************/
   11512             : 
   11513             : /** Return the list of attributes contained in this group.
   11514             :  *
   11515             :  * The returned array must be freed with GDALReleaseAttributes(). If only the
   11516             :  * array itself needs to be freed, CPLFree() should be called (and
   11517             :  * GDALAttributeRelease() on individual array members).
   11518             :  *
   11519             :  * This is the same as the C++ method GDALGroup::GetAttributes().
   11520             :  *
   11521             :  * @param hGroup Group.
   11522             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11523             :  * @param papszOptions Driver specific options determining how attributes
   11524             :  * should be retrieved. Pass nullptr for default behavior.
   11525             :  *
   11526             :  * @return an array of *pnCount attributes.
   11527             :  */
   11528          71 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
   11529             :                                        CSLConstList papszOptions)
   11530             : {
   11531          71 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11532          71 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11533          71 :     auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
   11534             :     auto ret = static_cast<GDALAttributeH *>(
   11535          71 :         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
   11536         229 :     for (size_t i = 0; i < attrs.size(); i++)
   11537             :     {
   11538         158 :         ret[i] = new GDALAttributeHS(attrs[i]);
   11539             :     }
   11540          71 :     *pnCount = attrs.size();
   11541          71 :     return ret;
   11542             : }
   11543             : 
   11544             : /************************************************************************/
   11545             : /*                     GDALGroupGetStructuralInfo()                     */
   11546             : /************************************************************************/
   11547             : 
   11548             : /** Return structural information on the group.
   11549             :  *
   11550             :  * This may be the compression, etc..
   11551             :  *
   11552             :  * The return value should not be freed and is valid until GDALGroup is
   11553             :  * released or this function called again.
   11554             :  *
   11555             :  * This is the same as the C++ method GDALGroup::GetStructuralInfo().
   11556             :  */
   11557           4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
   11558             : {
   11559           4 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11560           4 :     return hGroup->m_poImpl->GetStructuralInfo();
   11561             : }
   11562             : 
   11563             : /************************************************************************/
   11564             : /*                   GDALGroupGetDataTypeCount()                        */
   11565             : /************************************************************************/
   11566             : 
   11567             : /** Return the number of data types associated with the group
   11568             :  * (typically enumerations).
   11569             :  *
   11570             :  * This is the same as the C++ method GDALGroup::GetDataTypes().size().
   11571             :  *
   11572             :  * @since 3.12
   11573             :  */
   11574           4 : size_t GDALGroupGetDataTypeCount(GDALGroupH hGroup)
   11575             : {
   11576           4 :     VALIDATE_POINTER1(hGroup, __func__, 0);
   11577           4 :     return hGroup->m_poImpl->GetDataTypes().size();
   11578             : }
   11579             : 
   11580             : /************************************************************************/
   11581             : /*                      GDALGroupGetDataType()                          */
   11582             : /************************************************************************/
   11583             : 
   11584             : /** Return one of the data types associated with the group.
   11585             :  *
   11586             :  * This is the same as the C++ method GDALGroup::GetDataTypes()[].
   11587             :  *
   11588             :  * @return a type to release with GDALExtendedDataTypeRelease() once done,
   11589             :  * or nullptr in case of error.
   11590             :  * @since 3.12
   11591             :  */
   11592           1 : GDALExtendedDataTypeH GDALGroupGetDataType(GDALGroupH hGroup, size_t nIdx)
   11593             : {
   11594           1 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11595           1 :     if (nIdx >= hGroup->m_poImpl->GetDataTypes().size())
   11596           0 :         return nullptr;
   11597           1 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   11598           1 :         *(hGroup->m_poImpl->GetDataTypes()[nIdx].get())));
   11599             : }
   11600             : 
   11601             : /************************************************************************/
   11602             : /*                         GDALReleaseAttributes()                      */
   11603             : /************************************************************************/
   11604             : 
   11605             : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
   11606             :  *
   11607             :  * @param attributes return pointer of above methods
   11608             :  * @param nCount *pnCount value returned by above methods
   11609             :  */
   11610         129 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
   11611             : {
   11612         416 :     for (size_t i = 0; i < nCount; i++)
   11613             :     {
   11614         287 :         delete attributes[i];
   11615             :     }
   11616         129 :     CPLFree(attributes);
   11617         129 : }
   11618             : 
   11619             : /************************************************************************/
   11620             : /*                         GDALGroupCreateGroup()                       */
   11621             : /************************************************************************/
   11622             : 
   11623             : /** Create a sub-group within a group.
   11624             :  *
   11625             :  * This is the same as the C++ method GDALGroup::CreateGroup().
   11626             :  *
   11627             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11628             :  */
   11629         176 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11630             :                                 CSLConstList papszOptions)
   11631             : {
   11632         176 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11633         176 :     VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
   11634         528 :     auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
   11635         528 :                                              papszOptions);
   11636         176 :     if (!ret)
   11637          49 :         return nullptr;
   11638         127 :     return new GDALGroupHS(ret);
   11639             : }
   11640             : 
   11641             : /************************************************************************/
   11642             : /*                         GDALGroupDeleteGroup()                       */
   11643             : /************************************************************************/
   11644             : 
   11645             : /** Delete a sub-group from a group.
   11646             :  *
   11647             :  * After this call, if a previously obtained instance of the deleted object
   11648             :  * is still alive, no method other than for freeing it should be invoked.
   11649             :  *
   11650             :  * This is the same as the C++ method GDALGroup::DeleteGroup().
   11651             :  *
   11652             :  * @return true in case of success.
   11653             :  * @since GDAL 3.8
   11654             :  */
   11655          20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11656             :                           CSLConstList papszOptions)
   11657             : {
   11658          20 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11659          20 :     VALIDATE_POINTER1(pszSubGroupName, __func__, false);
   11660          40 :     return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
   11661          20 :                                          papszOptions);
   11662             : }
   11663             : 
   11664             : /************************************************************************/
   11665             : /*                      GDALGroupCreateDimension()                      */
   11666             : /************************************************************************/
   11667             : 
   11668             : /** Create a dimension within a group.
   11669             :  *
   11670             :  * This is the same as the C++ method GDALGroup::CreateDimension().
   11671             :  *
   11672             :  * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
   11673             :  */
   11674         664 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
   11675             :                                         const char *pszType,
   11676             :                                         const char *pszDirection, GUInt64 nSize,
   11677             :                                         CSLConstList papszOptions)
   11678             : {
   11679         664 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11680         664 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11681         664 :     auto ret = hGroup->m_poImpl->CreateDimension(
   11682        1328 :         std::string(pszName), std::string(pszType ? pszType : ""),
   11683        2656 :         std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
   11684         664 :     if (!ret)
   11685           9 :         return nullptr;
   11686         655 :     return new GDALDimensionHS(ret);
   11687             : }
   11688             : 
   11689             : /************************************************************************/
   11690             : /*                      GDALGroupCreateMDArray()                        */
   11691             : /************************************************************************/
   11692             : 
   11693             : /** Create a multidimensional array within a group.
   11694             :  *
   11695             :  * This is the same as the C++ method GDALGroup::CreateMDArray().
   11696             :  *
   11697             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11698             :  */
   11699         607 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
   11700             :                                     size_t nDimensions,
   11701             :                                     GDALDimensionH *pahDimensions,
   11702             :                                     GDALExtendedDataTypeH hEDT,
   11703             :                                     CSLConstList papszOptions)
   11704             : {
   11705         607 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11706         607 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11707         607 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11708        1214 :     std::vector<std::shared_ptr<GDALDimension>> dims;
   11709         607 :     dims.reserve(nDimensions);
   11710        1427 :     for (size_t i = 0; i < nDimensions; i++)
   11711         820 :         dims.push_back(pahDimensions[i]->m_poImpl);
   11712        1821 :     auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
   11713        1821 :                                                *(hEDT->m_poImpl), papszOptions);
   11714         607 :     if (!ret)
   11715          65 :         return nullptr;
   11716         542 :     return new GDALMDArrayHS(ret);
   11717             : }
   11718             : 
   11719             : /************************************************************************/
   11720             : /*                         GDALGroupDeleteMDArray()                     */
   11721             : /************************************************************************/
   11722             : 
   11723             : /** Delete an array from a group.
   11724             :  *
   11725             :  * After this call, if a previously obtained instance of the deleted object
   11726             :  * is still alive, no method other than for freeing it should be invoked.
   11727             :  *
   11728             :  * This is the same as the C++ method GDALGroup::DeleteMDArray().
   11729             :  *
   11730             :  * @return true in case of success.
   11731             :  * @since GDAL 3.8
   11732             :  */
   11733          20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
   11734             :                             CSLConstList papszOptions)
   11735             : {
   11736          20 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11737          20 :     VALIDATE_POINTER1(pszName, __func__, false);
   11738          20 :     return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
   11739             : }
   11740             : 
   11741             : /************************************************************************/
   11742             : /*                      GDALGroupCreateAttribute()                      */
   11743             : /************************************************************************/
   11744             : 
   11745             : /** Create a attribute within a group.
   11746             :  *
   11747             :  * This is the same as the C++ method GDALGroup::CreateAttribute().
   11748             :  *
   11749             :  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
   11750             :  */
   11751         120 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
   11752             :                                         size_t nDimensions,
   11753             :                                         const GUInt64 *panDimensions,
   11754             :                                         GDALExtendedDataTypeH hEDT,
   11755             :                                         CSLConstList papszOptions)
   11756             : {
   11757         120 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11758         120 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11759         240 :     std::vector<GUInt64> dims;
   11760         120 :     dims.reserve(nDimensions);
   11761         166 :     for (size_t i = 0; i < nDimensions; i++)
   11762          46 :         dims.push_back(panDimensions[i]);
   11763         120 :     auto ret = hGroup->m_poImpl->CreateAttribute(
   11764         360 :         std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
   11765         120 :     if (!ret)
   11766          14 :         return nullptr;
   11767         106 :     return new GDALAttributeHS(ret);
   11768             : }
   11769             : 
   11770             : /************************************************************************/
   11771             : /*                         GDALGroupDeleteAttribute()                   */
   11772             : /************************************************************************/
   11773             : 
   11774             : /** Delete an attribute from a group.
   11775             :  *
   11776             :  * After this call, if a previously obtained instance of the deleted object
   11777             :  * is still alive, no method other than for freeing it should be invoked.
   11778             :  *
   11779             :  * This is the same as the C++ method GDALGroup::DeleteAttribute().
   11780             :  *
   11781             :  * @return true in case of success.
   11782             :  * @since GDAL 3.8
   11783             :  */
   11784          25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
   11785             :                               CSLConstList papszOptions)
   11786             : {
   11787          25 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11788          25 :     VALIDATE_POINTER1(pszName, __func__, false);
   11789          50 :     return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
   11790          25 :                                              papszOptions);
   11791             : }
   11792             : 
   11793             : /************************************************************************/
   11794             : /*                          GDALGroupRename()                           */
   11795             : /************************************************************************/
   11796             : 
   11797             : /** Rename the group.
   11798             :  *
   11799             :  * This is not implemented by all drivers.
   11800             :  *
   11801             :  * Drivers known to implement it: MEM, netCDF.
   11802             :  *
   11803             :  * This is the same as the C++ method GDALGroup::Rename()
   11804             :  *
   11805             :  * @return true in case of success
   11806             :  * @since GDAL 3.8
   11807             :  */
   11808          45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
   11809             : {
   11810          45 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11811          45 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   11812          45 :     return hGroup->m_poImpl->Rename(pszNewName);
   11813             : }
   11814             : 
   11815             : /************************************************************************/
   11816             : /*                 GDALGroupSubsetDimensionFromSelection()              */
   11817             : /************************************************************************/
   11818             : 
   11819             : /** Return a virtual group whose one dimension has been subset according to a
   11820             :  * selection.
   11821             :  *
   11822             :  * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
   11823             :  *
   11824             :  * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
   11825             :  */
   11826             : GDALGroupH
   11827          14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
   11828             :                                       const char *pszSelection,
   11829             :                                       CPL_UNUSED CSLConstList papszOptions)
   11830             : {
   11831          14 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11832          14 :     VALIDATE_POINTER1(pszSelection, __func__, nullptr);
   11833          14 :     auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
   11834          42 :         std::string(pszSelection));
   11835          14 :     if (!hNewGroup)
   11836           8 :         return nullptr;
   11837           6 :     return new GDALGroupHS(hNewGroup);
   11838             : }
   11839             : 
   11840             : /************************************************************************/
   11841             : /*                        GDALMDArrayRelease()                          */
   11842             : /************************************************************************/
   11843             : 
   11844             : /** Release the GDAL in-memory object associated with a GDALMDArray.
   11845             :  *
   11846             :  * Note: when applied on a object coming from a driver, this does not
   11847             :  * destroy the object in the file, database, etc...
   11848             :  */
   11849        2015 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
   11850             : {
   11851        2015 :     delete hMDArray;
   11852        2015 : }
   11853             : 
   11854             : /************************************************************************/
   11855             : /*                        GDALMDArrayGetName()                          */
   11856             : /************************************************************************/
   11857             : 
   11858             : /** Return array name.
   11859             :  *
   11860             :  * This is the same as the C++ method GDALMDArray::GetName()
   11861             :  */
   11862          83 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
   11863             : {
   11864          83 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11865          83 :     return hArray->m_poImpl->GetName().c_str();
   11866             : }
   11867             : 
   11868             : /************************************************************************/
   11869             : /*                    GDALMDArrayGetFullName()                          */
   11870             : /************************************************************************/
   11871             : 
   11872             : /** Return array full name.
   11873             :  *
   11874             :  * This is the same as the C++ method GDALMDArray::GetFullName()
   11875             :  */
   11876          50 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
   11877             : {
   11878          50 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11879          50 :     return hArray->m_poImpl->GetFullName().c_str();
   11880             : }
   11881             : 
   11882             : /************************************************************************/
   11883             : /*                        GDALMDArrayGetName()                          */
   11884             : /************************************************************************/
   11885             : 
   11886             : /** Return the total number of values in the array.
   11887             :  *
   11888             :  * This is the same as the C++ method
   11889             :  * GDALAbstractMDArray::GetTotalElementsCount()
   11890             :  */
   11891           6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
   11892             : {
   11893           6 :     VALIDATE_POINTER1(hArray, __func__, 0);
   11894           6 :     return hArray->m_poImpl->GetTotalElementsCount();
   11895             : }
   11896             : 
   11897             : /************************************************************************/
   11898             : /*                        GDALMDArrayGetDimensionCount()                */
   11899             : /************************************************************************/
   11900             : 
   11901             : /** Return the number of dimensions.
   11902             :  *
   11903             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
   11904             :  */
   11905       10368 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
   11906             : {
   11907       10368 :     VALIDATE_POINTER1(hArray, __func__, 0);
   11908       10368 :     return hArray->m_poImpl->GetDimensionCount();
   11909             : }
   11910             : 
   11911             : /************************************************************************/
   11912             : /*                        GDALMDArrayGetDimensions()                    */
   11913             : /************************************************************************/
   11914             : 
   11915             : /** Return the dimensions of the array
   11916             :  *
   11917             :  * The returned array must be freed with GDALReleaseDimensions(). If only the
   11918             :  * array itself needs to be freed, CPLFree() should be called (and
   11919             :  * GDALDimensionRelease() on individual array members).
   11920             :  *
   11921             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
   11922             :  *
   11923             :  * @param hArray Array.
   11924             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11925             :  *
   11926             :  * @return an array of *pnCount dimensions.
   11927             :  */
   11928        2299 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
   11929             : {
   11930        2299 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11931        2299 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11932        2299 :     const auto &dims(hArray->m_poImpl->GetDimensions());
   11933             :     auto ret = static_cast<GDALDimensionH *>(
   11934        2299 :         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
   11935        6465 :     for (size_t i = 0; i < dims.size(); i++)
   11936             :     {
   11937        4166 :         ret[i] = new GDALDimensionHS(dims[i]);
   11938             :     }
   11939        2299 :     *pnCount = dims.size();
   11940        2299 :     return ret;
   11941             : }
   11942             : 
   11943             : /************************************************************************/
   11944             : /*                        GDALReleaseDimensions()                       */
   11945             : /************************************************************************/
   11946             : 
   11947             : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
   11948             :  *
   11949             :  * @param dims return pointer of above methods
   11950             :  * @param nCount *pnCount value returned by above methods
   11951             :  */
   11952        2372 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
   11953             : {
   11954        6695 :     for (size_t i = 0; i < nCount; i++)
   11955             :     {
   11956        4323 :         delete dims[i];
   11957             :     }
   11958        2372 :     CPLFree(dims);
   11959        2372 : }
   11960             : 
   11961             : /************************************************************************/
   11962             : /*                        GDALMDArrayGetDataType()                     */
   11963             : /************************************************************************/
   11964             : 
   11965             : /** Return the data type
   11966             :  *
   11967             :  * The return must be freed with GDALExtendedDataTypeRelease().
   11968             :  */
   11969        3909 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
   11970             : {
   11971        3909 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11972             :     return new GDALExtendedDataTypeHS(
   11973        3909 :         new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
   11974             : }
   11975             : 
   11976             : /************************************************************************/
   11977             : /*                          GDALMDArrayRead()                           */
   11978             : /************************************************************************/
   11979             : 
   11980             : /** Read part or totality of a multidimensional array.
   11981             :  *
   11982             :  * This is the same as the C++ method GDALAbstractMDArray::Read()
   11983             :  *
   11984             :  * @return TRUE in case of success.
   11985             :  */
   11986        1956 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   11987             :                     const size_t *count, const GInt64 *arrayStep,
   11988             :                     const GPtrDiff_t *bufferStride,
   11989             :                     GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
   11990             :                     const void *pDstBufferAllocStart,
   11991             :                     size_t nDstBufferAllocSize)
   11992             : {
   11993        1956 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11994        1956 :     if ((arrayStartIdx == nullptr || count == nullptr) &&
   11995           0 :         hArray->m_poImpl->GetDimensionCount() > 0)
   11996             :     {
   11997           0 :         VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
   11998           0 :         VALIDATE_POINTER1(count, __func__, FALSE);
   11999             :     }
   12000        1956 :     VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
   12001        1956 :     VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
   12002             :     // coverity[var_deref_model]
   12003        3912 :     return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
   12004        1956 :                                   *(bufferDataType->m_poImpl), pDstBuffer,
   12005        1956 :                                   pDstBufferAllocStart, nDstBufferAllocSize);
   12006             : }
   12007             : 
   12008             : /************************************************************************/
   12009             : /*                          GDALMDArrayWrite()                           */
   12010             : /************************************************************************/
   12011             : 
   12012             : /** Write part or totality of a multidimensional array.
   12013             :  *
   12014             :  * This is the same as the C++ method GDALAbstractMDArray::Write()
   12015             :  *
   12016             :  * @return TRUE in case of success.
   12017             :  */
   12018         558 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   12019             :                      const size_t *count, const GInt64 *arrayStep,
   12020             :                      const GPtrDiff_t *bufferStride,
   12021             :                      GDALExtendedDataTypeH bufferDataType,
   12022             :                      const void *pSrcBuffer, const void *pSrcBufferAllocStart,
   12023             :                      size_t nSrcBufferAllocSize)
   12024             : {
   12025         558 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12026         558 :     if ((arrayStartIdx == nullptr || count == nullptr) &&
   12027           0 :         hArray->m_poImpl->GetDimensionCount() > 0)
   12028             :     {
   12029           0 :         VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
   12030           0 :         VALIDATE_POINTER1(count, __func__, FALSE);
   12031             :     }
   12032         558 :     VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
   12033         558 :     VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
   12034             :     // coverity[var_deref_model]
   12035        1116 :     return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
   12036         558 :                                    bufferStride, *(bufferDataType->m_poImpl),
   12037             :                                    pSrcBuffer, pSrcBufferAllocStart,
   12038         558 :                                    nSrcBufferAllocSize);
   12039             : }
   12040             : 
   12041             : /************************************************************************/
   12042             : /*                       GDALMDArrayAdviseRead()                        */
   12043             : /************************************************************************/
   12044             : 
   12045             : /** Advise driver of upcoming read requests.
   12046             :  *
   12047             :  * This is the same as the C++ method GDALMDArray::AdviseRead()
   12048             :  *
   12049             :  * @return TRUE in case of success.
   12050             :  *
   12051             :  * @since GDAL 3.2
   12052             :  */
   12053           0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   12054             :                           const size_t *count)
   12055             : {
   12056           0 :     return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
   12057             : }
   12058             : 
   12059             : /************************************************************************/
   12060             : /*                      GDALMDArrayAdviseReadEx()                       */
   12061             : /************************************************************************/
   12062             : 
   12063             : /** Advise driver of upcoming read requests.
   12064             :  *
   12065             :  * This is the same as the C++ method GDALMDArray::AdviseRead()
   12066             :  *
   12067             :  * @return TRUE in case of success.
   12068             :  *
   12069             :  * @since GDAL 3.4
   12070             :  */
   12071          22 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   12072             :                             const size_t *count, CSLConstList papszOptions)
   12073             : {
   12074          22 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12075             :     // coverity[var_deref_model]
   12076          22 :     return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
   12077             : }
   12078             : 
   12079             : /************************************************************************/
   12080             : /*                         GDALMDArrayGetAttribute()                    */
   12081             : /************************************************************************/
   12082             : 
   12083             : /** Return an attribute by its name.
   12084             :  *
   12085             :  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
   12086             :  *
   12087             :  * The returned attribute must be freed with GDALAttributeRelease().
   12088             :  */
   12089         119 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
   12090             : {
   12091         119 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12092         119 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   12093         357 :     auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
   12094         119 :     if (attr)
   12095         110 :         return new GDALAttributeHS(attr);
   12096           9 :     return nullptr;
   12097             : }
   12098             : 
   12099             : /************************************************************************/
   12100             : /*                        GDALMDArrayGetAttributes()                    */
   12101             : /************************************************************************/
   12102             : 
   12103             : /** Return the list of attributes contained in this array.
   12104             :  *
   12105             :  * The returned array must be freed with GDALReleaseAttributes(). If only the
   12106             :  * array itself needs to be freed, CPLFree() should be called (and
   12107             :  * GDALAttributeRelease() on individual array members).
   12108             :  *
   12109             :  * This is the same as the C++ method GDALMDArray::GetAttributes().
   12110             :  *
   12111             :  * @param hArray Array.
   12112             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12113             :  * @param papszOptions Driver specific options determining how attributes
   12114             :  * should be retrieved. Pass nullptr for default behavior.
   12115             :  *
   12116             :  * @return an array of *pnCount attributes.
   12117             :  */
   12118          58 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
   12119             :                                          CSLConstList papszOptions)
   12120             : {
   12121          58 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12122          58 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12123          58 :     auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
   12124             :     auto ret = static_cast<GDALAttributeH *>(
   12125          58 :         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
   12126         187 :     for (size_t i = 0; i < attrs.size(); i++)
   12127             :     {
   12128         129 :         ret[i] = new GDALAttributeHS(attrs[i]);
   12129             :     }
   12130          58 :     *pnCount = attrs.size();
   12131          58 :     return ret;
   12132             : }
   12133             : 
   12134             : /************************************************************************/
   12135             : /*                       GDALMDArrayCreateAttribute()                   */
   12136             : /************************************************************************/
   12137             : 
   12138             : /** Create a attribute within an array.
   12139             :  *
   12140             :  * This is the same as the C++ method GDALMDArray::CreateAttribute().
   12141             :  *
   12142             :  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
   12143             :  */
   12144         150 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
   12145             :                                           const char *pszName,
   12146             :                                           size_t nDimensions,
   12147             :                                           const GUInt64 *panDimensions,
   12148             :                                           GDALExtendedDataTypeH hEDT,
   12149             :                                           CSLConstList papszOptions)
   12150             : {
   12151         150 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12152         150 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   12153         150 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   12154         300 :     std::vector<GUInt64> dims;
   12155         150 :     dims.reserve(nDimensions);
   12156         175 :     for (size_t i = 0; i < nDimensions; i++)
   12157          25 :         dims.push_back(panDimensions[i]);
   12158         150 :     auto ret = hArray->m_poImpl->CreateAttribute(
   12159         450 :         std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
   12160         150 :     if (!ret)
   12161           9 :         return nullptr;
   12162         141 :     return new GDALAttributeHS(ret);
   12163             : }
   12164             : 
   12165             : /************************************************************************/
   12166             : /*                       GDALMDArrayDeleteAttribute()                   */
   12167             : /************************************************************************/
   12168             : 
   12169             : /** Delete an attribute from an array.
   12170             :  *
   12171             :  * After this call, if a previously obtained instance of the deleted object
   12172             :  * is still alive, no method other than for freeing it should be invoked.
   12173             :  *
   12174             :  * This is the same as the C++ method GDALMDArray::DeleteAttribute().
   12175             :  *
   12176             :  * @return true in case of success.
   12177             :  * @since GDAL 3.8
   12178             :  */
   12179          24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
   12180             :                                 CSLConstList papszOptions)
   12181             : {
   12182          24 :     VALIDATE_POINTER1(hArray, __func__, false);
   12183          24 :     VALIDATE_POINTER1(pszName, __func__, false);
   12184          48 :     return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
   12185          24 :                                              papszOptions);
   12186             : }
   12187             : 
   12188             : /************************************************************************/
   12189             : /*                       GDALMDArrayGetRawNoDataValue()                 */
   12190             : /************************************************************************/
   12191             : 
   12192             : /** Return the nodata value as a "raw" value.
   12193             :  *
   12194             :  * The value returned might be nullptr in case of no nodata value. When
   12195             :  * a nodata value is registered, a non-nullptr will be returned whose size in
   12196             :  * bytes is GetDataType().GetSize().
   12197             :  *
   12198             :  * The returned value should not be modified or freed.
   12199             :  *
   12200             :  * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
   12201             :  *
   12202             :  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
   12203             :  */
   12204          76 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
   12205             : {
   12206          76 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12207          76 :     return hArray->m_poImpl->GetRawNoDataValue();
   12208             : }
   12209             : 
   12210             : /************************************************************************/
   12211             : /*                      GDALMDArrayGetNoDataValueAsDouble()             */
   12212             : /************************************************************************/
   12213             : 
   12214             : /** Return the nodata value as a double.
   12215             :  *
   12216             :  * The value returned might be nullptr in case of no nodata value. When
   12217             :  * a nodata value is registered, a non-nullptr will be returned whose size in
   12218             :  * bytes is GetDataType().GetSize().
   12219             :  *
   12220             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
   12221             :  *
   12222             :  * @param hArray Array handle.
   12223             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12224             :  * if a nodata value exists and can be converted to double. Might be nullptr.
   12225             :  *
   12226             :  * @return the nodata value as a double. A 0.0 value might also indicate the
   12227             :  * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
   12228             :  * will be set to false then).
   12229             :  */
   12230         120 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
   12231             :                                          int *pbHasNoDataValue)
   12232             : {
   12233         120 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12234         120 :     bool bHasNodataValue = false;
   12235         120 :     double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
   12236         120 :     if (pbHasNoDataValue)
   12237         120 :         *pbHasNoDataValue = bHasNodataValue;
   12238         120 :     return ret;
   12239             : }
   12240             : 
   12241             : /************************************************************************/
   12242             : /*                      GDALMDArrayGetNoDataValueAsInt64()              */
   12243             : /************************************************************************/
   12244             : 
   12245             : /** Return the nodata value as a Int64.
   12246             :  *
   12247             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
   12248             :  *
   12249             :  * @param hArray Array handle.
   12250             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12251             :  * if a nodata value exists and can be converted to Int64. Might be nullptr.
   12252             :  *
   12253             :  * @return the nodata value as a Int64.
   12254             :  * @since GDAL 3.5
   12255             :  */
   12256          11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
   12257             :                                          int *pbHasNoDataValue)
   12258             : {
   12259          11 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12260          11 :     bool bHasNodataValue = false;
   12261          11 :     const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
   12262          11 :     if (pbHasNoDataValue)
   12263          11 :         *pbHasNoDataValue = bHasNodataValue;
   12264          11 :     return ret;
   12265             : }
   12266             : 
   12267             : /************************************************************************/
   12268             : /*                      GDALMDArrayGetNoDataValueAsUInt64()              */
   12269             : /************************************************************************/
   12270             : 
   12271             : /** Return the nodata value as a UInt64.
   12272             :  *
   12273             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
   12274             :  *
   12275             :  * @param hArray Array handle.
   12276             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12277             :  * if a nodata value exists and can be converted to UInt64. Might be nullptr.
   12278             :  *
   12279             :  * @return the nodata value as a UInt64.
   12280             :  * @since GDAL 3.5
   12281             :  */
   12282           7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
   12283             :                                            int *pbHasNoDataValue)
   12284             : {
   12285           7 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12286           7 :     bool bHasNodataValue = false;
   12287           7 :     const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
   12288           7 :     if (pbHasNoDataValue)
   12289           7 :         *pbHasNoDataValue = bHasNodataValue;
   12290           7 :     return ret;
   12291             : }
   12292             : 
   12293             : /************************************************************************/
   12294             : /*                     GDALMDArraySetRawNoDataValue()                   */
   12295             : /************************************************************************/
   12296             : 
   12297             : /** Set the nodata value as a "raw" value.
   12298             :  *
   12299             :  * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
   12300             :  * void*).
   12301             :  *
   12302             :  * @return TRUE in case of success.
   12303             :  */
   12304          14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
   12305             : {
   12306          14 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12307          14 :     return hArray->m_poImpl->SetRawNoDataValue(pNoData);
   12308             : }
   12309             : 
   12310             : /************************************************************************/
   12311             : /*                   GDALMDArraySetNoDataValueAsDouble()                */
   12312             : /************************************************************************/
   12313             : 
   12314             : /** Set the nodata value as a double.
   12315             :  *
   12316             :  * If the natural data type of the attribute/array is not double, type
   12317             :  * conversion will occur to the type returned by GetDataType().
   12318             :  *
   12319             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
   12320             :  *
   12321             :  * @return TRUE in case of success.
   12322             :  */
   12323          51 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
   12324             : {
   12325          51 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12326          51 :     return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
   12327             : }
   12328             : 
   12329             : /************************************************************************/
   12330             : /*                   GDALMDArraySetNoDataValueAsInt64()                 */
   12331             : /************************************************************************/
   12332             : 
   12333             : /** Set the nodata value as a Int64.
   12334             :  *
   12335             :  * If the natural data type of the attribute/array is not Int64, type conversion
   12336             :  * will occur to the type returned by GetDataType().
   12337             :  *
   12338             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
   12339             :  *
   12340             :  * @return TRUE in case of success.
   12341             :  * @since GDAL 3.5
   12342             :  */
   12343           1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
   12344             : {
   12345           1 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12346           1 :     return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
   12347             : }
   12348             : 
   12349             : /************************************************************************/
   12350             : /*                   GDALMDArraySetNoDataValueAsUInt64()                */
   12351             : /************************************************************************/
   12352             : 
   12353             : /** Set the nodata value as a UInt64.
   12354             :  *
   12355             :  * If the natural data type of the attribute/array is not UInt64, type
   12356             :  * conversion will occur to the type returned by GetDataType().
   12357             :  *
   12358             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
   12359             :  *
   12360             :  * @return TRUE in case of success.
   12361             :  * @since GDAL 3.5
   12362             :  */
   12363           1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
   12364             :                                       uint64_t nNoDataValue)
   12365             : {
   12366           1 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12367           1 :     return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
   12368             : }
   12369             : 
   12370             : /************************************************************************/
   12371             : /*                        GDALMDArrayResize()                           */
   12372             : /************************************************************************/
   12373             : 
   12374             : /** Resize an array to new dimensions.
   12375             :  *
   12376             :  * Not all drivers may allow this operation, and with restrictions (e.g.
   12377             :  * for netCDF, this is limited to growing of "unlimited" dimensions)
   12378             :  *
   12379             :  * Resizing a dimension used in other arrays will cause those other arrays
   12380             :  * to be resized.
   12381             :  *
   12382             :  * This is the same as the C++ method GDALMDArray::Resize().
   12383             :  *
   12384             :  * @param hArray Array.
   12385             :  * @param panNewDimSizes Array of GetDimensionCount() values containing the
   12386             :  *                       new size of each indexing dimension.
   12387             :  * @param papszOptions Options. (Driver specific)
   12388             :  * @return true in case of success.
   12389             :  * @since GDAL 3.7
   12390             :  */
   12391          42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
   12392             :                        CSLConstList papszOptions)
   12393             : {
   12394          42 :     VALIDATE_POINTER1(hArray, __func__, false);
   12395          42 :     VALIDATE_POINTER1(panNewDimSizes, __func__, false);
   12396          84 :     std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
   12397         125 :     for (size_t i = 0; i < anNewDimSizes.size(); ++i)
   12398             :     {
   12399          83 :         anNewDimSizes[i] = panNewDimSizes[i];
   12400             :     }
   12401          42 :     return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
   12402             : }
   12403             : 
   12404             : /************************************************************************/
   12405             : /*                          GDALMDArraySetScale()                       */
   12406             : /************************************************************************/
   12407             : 
   12408             : /** Set the scale value to apply to raw values.
   12409             :  *
   12410             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12411             :  *
   12412             :  * This is the same as the C++ method GDALMDArray::SetScale().
   12413             :  *
   12414             :  * @return TRUE in case of success.
   12415             :  */
   12416           0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
   12417             : {
   12418           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12419           0 :     return hArray->m_poImpl->SetScale(dfScale);
   12420             : }
   12421             : 
   12422             : /************************************************************************/
   12423             : /*                        GDALMDArraySetScaleEx()                       */
   12424             : /************************************************************************/
   12425             : 
   12426             : /** Set the scale value to apply to raw values.
   12427             :  *
   12428             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12429             :  *
   12430             :  * This is the same as the C++ method GDALMDArray::SetScale().
   12431             :  *
   12432             :  * @return TRUE in case of success.
   12433             :  * @since GDAL 3.3
   12434             :  */
   12435          21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
   12436             :                           GDALDataType eStorageType)
   12437             : {
   12438          21 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12439          21 :     return hArray->m_poImpl->SetScale(dfScale, eStorageType);
   12440             : }
   12441             : 
   12442             : /************************************************************************/
   12443             : /*                          GDALMDArraySetOffset()                       */
   12444             : /************************************************************************/
   12445             : 
   12446             : /** Set the scale value to apply to raw values.
   12447             :  *
   12448             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12449             :  *
   12450             :  * This is the same as the C++ method GDALMDArray::SetOffset().
   12451             :  *
   12452             :  * @return TRUE in case of success.
   12453             :  */
   12454           0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
   12455             : {
   12456           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12457           0 :     return hArray->m_poImpl->SetOffset(dfOffset);
   12458             : }
   12459             : 
   12460             : /************************************************************************/
   12461             : /*                       GDALMDArraySetOffsetEx()                       */
   12462             : /************************************************************************/
   12463             : 
   12464             : /** Set the scale value to apply to raw values.
   12465             :  *
   12466             :  * unscaled_value = raw_value * GetOffset() + GetOffset()
   12467             :  *
   12468             :  * This is the same as the C++ method GDALMDArray::SetOffset().
   12469             :  *
   12470             :  * @return TRUE in case of success.
   12471             :  * @since GDAL 3.3
   12472             :  */
   12473          21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
   12474             :                            GDALDataType eStorageType)
   12475             : {
   12476          21 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12477          21 :     return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
   12478             : }
   12479             : 
   12480             : /************************************************************************/
   12481             : /*                          GDALMDArrayGetScale()                       */
   12482             : /************************************************************************/
   12483             : 
   12484             : /** Get the scale value to apply to raw values.
   12485             :  *
   12486             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12487             :  *
   12488             :  * This is the same as the C++ method GDALMDArray::GetScale().
   12489             :  *
   12490             :  * @return the scale value
   12491             :  */
   12492         103 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
   12493             : {
   12494         103 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12495         103 :     bool bHasValue = false;
   12496         103 :     double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
   12497         103 :     if (pbHasValue)
   12498         103 :         *pbHasValue = bHasValue;
   12499         103 :     return dfRet;
   12500             : }
   12501             : 
   12502             : /************************************************************************/
   12503             : /*                        GDALMDArrayGetScaleEx()                       */
   12504             : /************************************************************************/
   12505             : 
   12506             : /** Get the scale value to apply to raw values.
   12507             :  *
   12508             :  * unscaled_value = raw_value * GetScale() + GetScale()
   12509             :  *
   12510             :  * This is the same as the C++ method GDALMDArray::GetScale().
   12511             :  *
   12512             :  * @return the scale value
   12513             :  * @since GDAL 3.3
   12514             :  */
   12515           5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
   12516             :                              GDALDataType *peStorageType)
   12517             : {
   12518           5 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12519           5 :     bool bHasValue = false;
   12520           5 :     double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
   12521           5 :     if (pbHasValue)
   12522           5 :         *pbHasValue = bHasValue;
   12523           5 :     return dfRet;
   12524             : }
   12525             : 
   12526             : /************************************************************************/
   12527             : /*                          GDALMDArrayGetOffset()                      */
   12528             : /************************************************************************/
   12529             : 
   12530             : /** Get the scale value to apply to raw values.
   12531             :  *
   12532             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12533             :  *
   12534             :  * This is the same as the C++ method GDALMDArray::GetOffset().
   12535             :  *
   12536             :  * @return the scale value
   12537             :  */
   12538         100 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
   12539             : {
   12540         100 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12541         100 :     bool bHasValue = false;
   12542         100 :     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
   12543         100 :     if (pbHasValue)
   12544         100 :         *pbHasValue = bHasValue;
   12545         100 :     return dfRet;
   12546             : }
   12547             : 
   12548             : /************************************************************************/
   12549             : /*                        GDALMDArrayGetOffsetEx()                      */
   12550             : /************************************************************************/
   12551             : 
   12552             : /** Get the scale value to apply to raw values.
   12553             :  *
   12554             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12555             :  *
   12556             :  * This is the same as the C++ method GDALMDArray::GetOffset().
   12557             :  *
   12558             :  * @return the scale value
   12559             :  * @since GDAL 3.3
   12560             :  */
   12561           5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
   12562             :                               GDALDataType *peStorageType)
   12563             : {
   12564           5 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12565           5 :     bool bHasValue = false;
   12566           5 :     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
   12567           5 :     if (pbHasValue)
   12568           5 :         *pbHasValue = bHasValue;
   12569           5 :     return dfRet;
   12570             : }
   12571             : 
   12572             : /************************************************************************/
   12573             : /*                      GDALMDArrayGetBlockSize()                       */
   12574             : /************************************************************************/
   12575             : 
   12576             : /** Return the "natural" block size of the array along all dimensions.
   12577             :  *
   12578             :  * Some drivers might organize the array in tiles/blocks and reading/writing
   12579             :  * aligned on those tile/block boundaries will be more efficient.
   12580             :  *
   12581             :  * The returned number of elements in the vector is the same as
   12582             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
   12583             :  * the natural block size along the considered dimension.
   12584             :  * "Flat" arrays will typically return a vector of values set to 0.
   12585             :  *
   12586             :  * The default implementation will return a vector of values set to 0.
   12587             :  *
   12588             :  * This method is used by GetProcessingChunkSize().
   12589             :  *
   12590             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
   12591             :  * theoretical case of a 32-bit platform, this might exceed its size_t
   12592             :  * allocation capabilities.
   12593             :  *
   12594             :  * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
   12595             :  *
   12596             :  * @return the block size, in number of elements along each dimension.
   12597             :  */
   12598          93 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
   12599             : {
   12600          93 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12601          93 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12602          93 :     auto res = hArray->m_poImpl->GetBlockSize();
   12603          93 :     auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
   12604         285 :     for (size_t i = 0; i < res.size(); i++)
   12605             :     {
   12606         192 :         ret[i] = res[i];
   12607             :     }
   12608          93 :     *pnCount = res.size();
   12609          93 :     return ret;
   12610             : }
   12611             : 
   12612             : /***********************************************************************/
   12613             : /*                   GDALMDArrayGetProcessingChunkSize()               */
   12614             : /************************************************************************/
   12615             : 
   12616             : /** \brief Return an optimal chunk size for read/write operations, given the
   12617             :  * natural block size and memory constraints specified.
   12618             :  *
   12619             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
   12620             :  * multiple of those returned by GetBlockSize() (unless the block define by
   12621             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
   12622             :  * returned by this method).
   12623             :  *
   12624             :  * This is the same as the C++ method
   12625             :  * GDALAbstractMDArray::GetProcessingChunkSize().
   12626             :  *
   12627             :  * @param hArray Array.
   12628             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12629             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
   12630             :  * chunk.
   12631             :  *
   12632             :  * @return the chunk size, in number of elements along each dimension.
   12633             :  */
   12634             : 
   12635           1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
   12636             :                                           size_t nMaxChunkMemory)
   12637             : {
   12638           1 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12639           1 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12640           1 :     auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
   12641           1 :     auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
   12642           3 :     for (size_t i = 0; i < res.size(); i++)
   12643             :     {
   12644           2 :         ret[i] = res[i];
   12645             :     }
   12646           1 :     *pnCount = res.size();
   12647           1 :     return ret;
   12648             : }
   12649             : 
   12650             : /************************************************************************/
   12651             : /*                     GDALMDArrayGetStructuralInfo()                   */
   12652             : /************************************************************************/
   12653             : 
   12654             : /** Return structural information on the array.
   12655             :  *
   12656             :  * This may be the compression, etc..
   12657             :  *
   12658             :  * The return value should not be freed and is valid until GDALMDArray is
   12659             :  * released or this function called again.
   12660             :  *
   12661             :  * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
   12662             :  */
   12663          15 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
   12664             : {
   12665          15 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12666          15 :     return hArray->m_poImpl->GetStructuralInfo();
   12667             : }
   12668             : 
   12669             : /************************************************************************/
   12670             : /*                        GDALMDArrayGetView()                          */
   12671             : /************************************************************************/
   12672             : 
   12673             : /** Return a view of the array using slicing or field access.
   12674             :  *
   12675             :  * The returned object should be released with GDALMDArrayRelease().
   12676             :  *
   12677             :  * This is the same as the C++ method GDALMDArray::GetView().
   12678             :  */
   12679         430 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
   12680             : {
   12681         430 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12682         430 :     VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
   12683        1290 :     auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
   12684         430 :     if (!sliced)
   12685          22 :         return nullptr;
   12686         408 :     return new GDALMDArrayHS(sliced);
   12687             : }
   12688             : 
   12689             : /************************************************************************/
   12690             : /*                       GDALMDArrayTranspose()                         */
   12691             : /************************************************************************/
   12692             : 
   12693             : /** Return a view of the array whose axis have been reordered.
   12694             :  *
   12695             :  * The returned object should be released with GDALMDArrayRelease().
   12696             :  *
   12697             :  * This is the same as the C++ method GDALMDArray::Transpose().
   12698             :  */
   12699          44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
   12700             :                                   const int *panMapNewAxisToOldAxis)
   12701             : {
   12702          44 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12703          88 :     std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
   12704          44 :     if (nNewAxisCount)
   12705             :     {
   12706          43 :         memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
   12707             :                nNewAxisCount * sizeof(int));
   12708             :     }
   12709          88 :     auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
   12710          44 :     if (!reordered)
   12711           7 :         return nullptr;
   12712          37 :     return new GDALMDArrayHS(reordered);
   12713             : }
   12714             : 
   12715             : /************************************************************************/
   12716             : /*                      GDALMDArrayGetUnscaled()                        */
   12717             : /************************************************************************/
   12718             : 
   12719             : /** Return an array that is the unscaled version of the current one.
   12720             :  *
   12721             :  * That is each value of the unscaled array will be
   12722             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12723             :  *
   12724             :  * Starting with GDAL 3.3, the Write() method is implemented and will convert
   12725             :  * from unscaled values to raw values.
   12726             :  *
   12727             :  * The returned object should be released with GDALMDArrayRelease().
   12728             :  *
   12729             :  * This is the same as the C++ method GDALMDArray::GetUnscaled().
   12730             :  */
   12731          13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
   12732             : {
   12733          13 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12734          26 :     auto unscaled = hArray->m_poImpl->GetUnscaled();
   12735          13 :     if (!unscaled)
   12736           0 :         return nullptr;
   12737          13 :     return new GDALMDArrayHS(unscaled);
   12738             : }
   12739             : 
   12740             : /************************************************************************/
   12741             : /*                          GDALMDArrayGetMask()                         */
   12742             : /************************************************************************/
   12743             : 
   12744             : /** Return an array that is a mask for the current array
   12745             :  *
   12746             :  * This array will be of type Byte, with values set to 0 to indicate invalid
   12747             :  * pixels of the current array, and values set to 1 to indicate valid pixels.
   12748             :  *
   12749             :  * The returned object should be released with GDALMDArrayRelease().
   12750             :  *
   12751             :  * This is the same as the C++ method GDALMDArray::GetMask().
   12752             :  */
   12753          35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
   12754             : {
   12755          35 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12756          70 :     auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
   12757          35 :     if (!unscaled)
   12758           7 :         return nullptr;
   12759          28 :     return new GDALMDArrayHS(unscaled);
   12760             : }
   12761             : 
   12762             : /************************************************************************/
   12763             : /*                   GDALMDArrayGetResampled()                          */
   12764             : /************************************************************************/
   12765             : 
   12766             : /** Return an array that is a resampled / reprojected view of the current array
   12767             :  *
   12768             :  * This is the same as the C++ method GDALMDArray::GetResampled().
   12769             :  *
   12770             :  * Currently this method can only resample along the last 2 dimensions, unless
   12771             :  * orthorectifying a NASA EMIT dataset.
   12772             :  *
   12773             :  * The returned object should be released with GDALMDArrayRelease().
   12774             :  *
   12775             :  * @since 3.4
   12776             :  */
   12777          34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
   12778             :                                      const GDALDimensionH *pahNewDims,
   12779             :                                      GDALRIOResampleAlg resampleAlg,
   12780             :                                      OGRSpatialReferenceH hTargetSRS,
   12781             :                                      CSLConstList papszOptions)
   12782             : {
   12783          34 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12784          34 :     VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
   12785          68 :     std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
   12786         112 :     for (size_t i = 0; i < nNewDimCount; ++i)
   12787             :     {
   12788          78 :         if (pahNewDims[i])
   12789           8 :             apoNewDims[i] = pahNewDims[i]->m_poImpl;
   12790             :     }
   12791          34 :     auto poNewArray = hArray->m_poImpl->GetResampled(
   12792          34 :         apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
   12793          68 :         papszOptions);
   12794          34 :     if (!poNewArray)
   12795           8 :         return nullptr;
   12796          26 :     return new GDALMDArrayHS(poNewArray);
   12797             : }
   12798             : 
   12799             : /************************************************************************/
   12800             : /*                      GDALMDArraySetUnit()                            */
   12801             : /************************************************************************/
   12802             : 
   12803             : /** Set the variable unit.
   12804             :  *
   12805             :  * Values should conform as much as possible with those allowed by
   12806             :  * the NetCDF CF conventions:
   12807             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
   12808             :  * but others might be returned.
   12809             :  *
   12810             :  * Few examples are "meter", "degrees", "second", ...
   12811             :  * Empty value means unknown.
   12812             :  *
   12813             :  * This is the same as the C function GDALMDArraySetUnit()
   12814             :  *
   12815             :  * @param hArray array.
   12816             :  * @param pszUnit unit name.
   12817             :  * @return TRUE in case of success.
   12818             :  */
   12819          15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
   12820             : {
   12821          15 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12822          15 :     return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
   12823             : }
   12824             : 
   12825             : /************************************************************************/
   12826             : /*                      GDALMDArrayGetUnit()                            */
   12827             : /************************************************************************/
   12828             : 
   12829             : /** Return the array unit.
   12830             :  *
   12831             :  * Values should conform as much as possible with those allowed by
   12832             :  * the NetCDF CF conventions:
   12833             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
   12834             :  * but others might be returned.
   12835             :  *
   12836             :  * Few examples are "meter", "degrees", "second", ...
   12837             :  * Empty value means unknown.
   12838             :  *
   12839             :  * The return value should not be freed and is valid until GDALMDArray is
   12840             :  * released or this function called again.
   12841             :  *
   12842             :  * This is the same as the C++ method GDALMDArray::GetUnit().
   12843             :  */
   12844         111 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
   12845             : {
   12846         111 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12847         111 :     return hArray->m_poImpl->GetUnit().c_str();
   12848             : }
   12849             : 
   12850             : /************************************************************************/
   12851             : /*                      GDALMDArrayGetSpatialRef()                      */
   12852             : /************************************************************************/
   12853             : 
   12854             : /** Assign a spatial reference system object to the array.
   12855             :  *
   12856             :  * This is the same as the C++ method GDALMDArray::SetSpatialRef().
   12857             :  * @return TRUE in case of success.
   12858             :  */
   12859          30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
   12860             : {
   12861          30 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12862          60 :     return hArray->m_poImpl->SetSpatialRef(
   12863          60 :         OGRSpatialReference::FromHandle(hSRS));
   12864             : }
   12865             : 
   12866             : /************************************************************************/
   12867             : /*                      GDALMDArrayGetSpatialRef()                      */
   12868             : /************************************************************************/
   12869             : 
   12870             : /** Return the spatial reference system object associated with the array.
   12871             :  *
   12872             :  * This is the same as the C++ method GDALMDArray::GetSpatialRef().
   12873             :  *
   12874             :  * The returned object must be freed with OSRDestroySpatialReference().
   12875             :  */
   12876          77 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
   12877             : {
   12878          77 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12879          77 :     auto poSRS = hArray->m_poImpl->GetSpatialRef();
   12880          77 :     return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
   12881             : }
   12882             : 
   12883             : /************************************************************************/
   12884             : /*                      GDALMDArrayGetStatistics()                      */
   12885             : /************************************************************************/
   12886             : 
   12887             : /**
   12888             :  * \brief Fetch statistics.
   12889             :  *
   12890             :  * This is the same as the C++ method GDALMDArray::GetStatistics().
   12891             :  *
   12892             :  * @since GDAL 3.2
   12893             :  */
   12894             : 
   12895          15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
   12896             :                                 int bApproxOK, int bForce, double *pdfMin,
   12897             :                                 double *pdfMax, double *pdfMean,
   12898             :                                 double *pdfStdDev, GUInt64 *pnValidCount,
   12899             :                                 GDALProgressFunc pfnProgress,
   12900             :                                 void *pProgressData)
   12901             : {
   12902          15 :     VALIDATE_POINTER1(hArray, __func__, CE_Failure);
   12903          30 :     return hArray->m_poImpl->GetStatistics(
   12904          15 :         CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
   12905          15 :         pdfStdDev, pnValidCount, pfnProgress, pProgressData);
   12906             : }
   12907             : 
   12908             : /************************************************************************/
   12909             : /*                      GDALMDArrayComputeStatistics()                  */
   12910             : /************************************************************************/
   12911             : 
   12912             : /**
   12913             :  * \brief Compute statistics.
   12914             :  *
   12915             :  * This is the same as the C++ method GDALMDArray::ComputeStatistics().
   12916             :  *
   12917             :  * @since GDAL 3.2
   12918             :  * @see GDALMDArrayComputeStatisticsEx()
   12919             :  */
   12920             : 
   12921           0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
   12922             :                                  int bApproxOK, double *pdfMin, double *pdfMax,
   12923             :                                  double *pdfMean, double *pdfStdDev,
   12924             :                                  GUInt64 *pnValidCount,
   12925             :                                  GDALProgressFunc pfnProgress,
   12926             :                                  void *pProgressData)
   12927             : {
   12928           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12929           0 :     return hArray->m_poImpl->ComputeStatistics(
   12930           0 :         CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
   12931           0 :         pnValidCount, pfnProgress, pProgressData, nullptr);
   12932             : }
   12933             : 
   12934             : /************************************************************************/
   12935             : /*                     GDALMDArrayComputeStatisticsEx()                 */
   12936             : /************************************************************************/
   12937             : 
   12938             : /**
   12939             :  * \brief Compute statistics.
   12940             :  *
   12941             :  * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
   12942             :  *
   12943             :  * This is the same as the C++ method GDALMDArray::ComputeStatistics().
   12944             :  *
   12945             :  * @since GDAL 3.8
   12946             :  */
   12947             : 
   12948           4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
   12949             :                                    int bApproxOK, double *pdfMin,
   12950             :                                    double *pdfMax, double *pdfMean,
   12951             :                                    double *pdfStdDev, GUInt64 *pnValidCount,
   12952             :                                    GDALProgressFunc pfnProgress,
   12953             :                                    void *pProgressData,
   12954             :                                    CSLConstList papszOptions)
   12955             : {
   12956           4 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12957           8 :     return hArray->m_poImpl->ComputeStatistics(
   12958           4 :         CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
   12959           8 :         pnValidCount, pfnProgress, pProgressData, papszOptions);
   12960             : }
   12961             : 
   12962             : /************************************************************************/
   12963             : /*                 GDALMDArrayGetCoordinateVariables()                  */
   12964             : /************************************************************************/
   12965             : 
   12966             : /** Return coordinate variables.
   12967             :  *
   12968             :  * The returned array must be freed with GDALReleaseArrays(). If only the array
   12969             :  * itself needs to be freed, CPLFree() should be called (and
   12970             :  * GDALMDArrayRelease() on individual array members).
   12971             :  *
   12972             :  * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
   12973             :  *
   12974             :  * @param hArray Array.
   12975             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12976             :  *
   12977             :  * @return an array of *pnCount arrays.
   12978             :  * @since 3.4
   12979             :  */
   12980          13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
   12981             :                                                 size_t *pnCount)
   12982             : {
   12983          13 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12984          13 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12985          13 :     const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
   12986             :     auto ret = static_cast<GDALMDArrayH *>(
   12987          13 :         CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
   12988          29 :     for (size_t i = 0; i < coordinates.size(); i++)
   12989             :     {
   12990          16 :         ret[i] = new GDALMDArrayHS(coordinates[i]);
   12991             :     }
   12992          13 :     *pnCount = coordinates.size();
   12993          13 :     return ret;
   12994             : }
   12995             : 
   12996             : /************************************************************************/
   12997             : /*                     GDALMDArrayGetGridded()                          */
   12998             : /************************************************************************/
   12999             : 
   13000             : /** Return a gridded array from scattered point data, that is from an array
   13001             :  * whose last dimension is the indexing variable of X and Y arrays.
   13002             :  *
   13003             :  * The returned object should be released with GDALMDArrayRelease().
   13004             :  *
   13005             :  * This is the same as the C++ method GDALMDArray::GetGridded().
   13006             :  *
   13007             :  * @since GDAL 3.7
   13008             :  */
   13009          22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
   13010             :                                    const char *pszGridOptions,
   13011             :                                    GDALMDArrayH hXArray, GDALMDArrayH hYArray,
   13012             :                                    CSLConstList papszOptions)
   13013             : {
   13014          22 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13015          22 :     VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
   13016          22 :     auto gridded = hArray->m_poImpl->GetGridded(
   13017          44 :         pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
   13018          88 :         hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
   13019          22 :     if (!gridded)
   13020          19 :         return nullptr;
   13021           3 :     return new GDALMDArrayHS(gridded);
   13022             : }
   13023             : 
   13024             : /************************************************************************/
   13025             : /*                      GDALMDArrayGetMeshGrid()                        */
   13026             : /************************************************************************/
   13027             : 
   13028             : /** Return a list of multidimensional arrays from a list of one-dimensional
   13029             :  * arrays.
   13030             :  *
   13031             :  * This is typically used to transform one-dimensional longitude, latitude
   13032             :  * arrays into 2D ones.
   13033             :  *
   13034             :  * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
   13035             :  * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
   13036             :  * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
   13037             :  * repeated to fill the matrix along the first dimension for x1, the second
   13038             :  * for x2 and so on.
   13039             :  *
   13040             :  * For example, if x = [1, 2], and y = [3, 4, 5],
   13041             :  * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
   13042             :  * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
   13043             :  * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
   13044             :  *
   13045             :  * and
   13046             :  * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
   13047             :  * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
   13048             :  * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
   13049             :  *
   13050             :  * The currently supported options are:
   13051             :  * <ul>
   13052             :  * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
   13053             :  * output.
   13054             :  * </li>
   13055             :  * </ul>
   13056             :  *
   13057             :  * This is the same as
   13058             :  * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
   13059             :  * function.
   13060             :  *
   13061             :  * The returned array (of arrays) must be freed with GDALReleaseArrays().
   13062             :  * If only the array itself needs to be freed, CPLFree() should be called
   13063             :  * (and GDALMDArrayRelease() on individual array members).
   13064             :  *
   13065             :  * This is the same as the C++ method GDALMDArray::GetMeshGrid()
   13066             :  *
   13067             :  * @param pahInputArrays Input arrays
   13068             :  * @param nCountInputArrays Number of input arrays
   13069             :  * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
   13070             :  * @param papszOptions NULL, or NULL terminated list of options.
   13071             :  *
   13072             :  * @return an array of *pnCountOutputArrays arrays.
   13073             :  * @since 3.10
   13074             :  */
   13075           7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
   13076             :                                      size_t nCountInputArrays,
   13077             :                                      size_t *pnCountOutputArrays,
   13078             :                                      CSLConstList papszOptions)
   13079             : {
   13080           7 :     VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
   13081           7 :     VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
   13082             : 
   13083          14 :     std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
   13084          20 :     for (size_t i = 0; i < nCountInputArrays; ++i)
   13085          13 :         apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
   13086             : 
   13087             :     const auto apoOutputArrays =
   13088           7 :         GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
   13089             :     auto ret = static_cast<GDALMDArrayH *>(
   13090           7 :         CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
   13091          17 :     for (size_t i = 0; i < apoOutputArrays.size(); i++)
   13092             :     {
   13093          10 :         ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
   13094             :     }
   13095           7 :     *pnCountOutputArrays = apoOutputArrays.size();
   13096           7 :     return ret;
   13097             : }
   13098             : 
   13099             : /************************************************************************/
   13100             : /*                        GDALReleaseArrays()                           */
   13101             : /************************************************************************/
   13102             : 
   13103             : /** Free the return of GDALMDArrayGetCoordinateVariables()
   13104             :  *
   13105             :  * @param arrays return pointer of above methods
   13106             :  * @param nCount *pnCount value returned by above methods
   13107             :  */
   13108          20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
   13109             : {
   13110          46 :     for (size_t i = 0; i < nCount; i++)
   13111             :     {
   13112          26 :         delete arrays[i];
   13113             :     }
   13114          20 :     CPLFree(arrays);
   13115          20 : }
   13116             : 
   13117             : /************************************************************************/
   13118             : /*                           GDALMDArrayCache()                         */
   13119             : /************************************************************************/
   13120             : 
   13121             : /**
   13122             :  * \brief Cache the content of the array into an auxiliary filename.
   13123             :  *
   13124             :  * This is the same as the C++ method GDALMDArray::Cache().
   13125             :  *
   13126             :  * @since GDAL 3.4
   13127             :  */
   13128             : 
   13129           7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
   13130             : {
   13131           7 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13132           7 :     return hArray->m_poImpl->Cache(papszOptions);
   13133             : }
   13134             : 
   13135             : /************************************************************************/
   13136             : /*                       GDALMDArrayRename()                           */
   13137             : /************************************************************************/
   13138             : 
   13139             : /** Rename the array.
   13140             :  *
   13141             :  * This is not implemented by all drivers.
   13142             :  *
   13143             :  * Drivers known to implement it: MEM, netCDF, Zarr.
   13144             :  *
   13145             :  * This is the same as the C++ method GDALAbstractMDArray::Rename()
   13146             :  *
   13147             :  * @return true in case of success
   13148             :  * @since GDAL 3.8
   13149             :  */
   13150          28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
   13151             : {
   13152          28 :     VALIDATE_POINTER1(hArray, __func__, false);
   13153          28 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   13154          28 :     return hArray->m_poImpl->Rename(pszNewName);
   13155             : }
   13156             : 
   13157             : /************************************************************************/
   13158             : /*                        GDALAttributeRelease()                        */
   13159             : /************************************************************************/
   13160             : 
   13161             : /** Release the GDAL in-memory object associated with a GDALAttribute.
   13162             :  *
   13163             :  * Note: when applied on a object coming from a driver, this does not
   13164             :  * destroy the object in the file, database, etc...
   13165             :  */
   13166         720 : void GDALAttributeRelease(GDALAttributeH hAttr)
   13167             : {
   13168         720 :     delete hAttr;
   13169         720 : }
   13170             : 
   13171             : /************************************************************************/
   13172             : /*                        GDALAttributeGetName()                        */
   13173             : /************************************************************************/
   13174             : 
   13175             : /** Return the name of the attribute.
   13176             :  *
   13177             :  * The returned pointer is valid until hAttr is released.
   13178             :  *
   13179             :  * This is the same as the C++ method GDALAttribute::GetName().
   13180             :  */
   13181         361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
   13182             : {
   13183         361 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13184         361 :     return hAttr->m_poImpl->GetName().c_str();
   13185             : }
   13186             : 
   13187             : /************************************************************************/
   13188             : /*                      GDALAttributeGetFullName()                      */
   13189             : /************************************************************************/
   13190             : 
   13191             : /** Return the full name of the attribute.
   13192             :  *
   13193             :  * The returned pointer is valid until hAttr is released.
   13194             :  *
   13195             :  * This is the same as the C++ method GDALAttribute::GetFullName().
   13196             :  */
   13197          49 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
   13198             : {
   13199          49 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13200          49 :     return hAttr->m_poImpl->GetFullName().c_str();
   13201             : }
   13202             : 
   13203             : /************************************************************************/
   13204             : /*                   GDALAttributeGetTotalElementsCount()               */
   13205             : /************************************************************************/
   13206             : 
   13207             : /** Return the total number of values in the attribute.
   13208             :  *
   13209             :  * This is the same as the C++ method
   13210             :  * GDALAbstractMDArray::GetTotalElementsCount()
   13211             :  */
   13212         176 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
   13213             : {
   13214         176 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13215         176 :     return hAttr->m_poImpl->GetTotalElementsCount();
   13216             : }
   13217             : 
   13218             : /************************************************************************/
   13219             : /*                    GDALAttributeGetDimensionCount()                */
   13220             : /************************************************************************/
   13221             : 
   13222             : /** Return the number of dimensions.
   13223             :  *
   13224             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
   13225             :  */
   13226          12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
   13227             : {
   13228          12 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13229          12 :     return hAttr->m_poImpl->GetDimensionCount();
   13230             : }
   13231             : 
   13232             : /************************************************************************/
   13233             : /*                       GDALAttributeGetDimensionsSize()                */
   13234             : /************************************************************************/
   13235             : 
   13236             : /** Return the dimension sizes of the attribute.
   13237             :  *
   13238             :  * The returned array must be freed with CPLFree()
   13239             :  *
   13240             :  * @param hAttr Attribute.
   13241             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13242             :  *
   13243             :  * @return an array of *pnCount values.
   13244             :  */
   13245          11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
   13246             : {
   13247          11 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13248          11 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13249          11 :     const auto &dims = hAttr->m_poImpl->GetDimensions();
   13250          11 :     auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
   13251          22 :     for (size_t i = 0; i < dims.size(); i++)
   13252             :     {
   13253          11 :         ret[i] = dims[i]->GetSize();
   13254             :     }
   13255          11 :     *pnCount = dims.size();
   13256          11 :     return ret;
   13257             : }
   13258             : 
   13259             : /************************************************************************/
   13260             : /*                       GDALAttributeGetDataType()                     */
   13261             : /************************************************************************/
   13262             : 
   13263             : /** Return the data type
   13264             :  *
   13265             :  * The return must be freed with GDALExtendedDataTypeRelease().
   13266             :  */
   13267         427 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
   13268             : {
   13269         427 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13270             :     return new GDALExtendedDataTypeHS(
   13271         427 :         new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
   13272             : }
   13273             : 
   13274             : /************************************************************************/
   13275             : /*                       GDALAttributeReadAsRaw()                       */
   13276             : /************************************************************************/
   13277             : 
   13278             : /** Return the raw value of an attribute.
   13279             :  *
   13280             :  * This is the same as the C++ method GDALAttribute::ReadAsRaw().
   13281             :  *
   13282             :  * The returned buffer must be freed with GDALAttributeFreeRawResult()
   13283             :  *
   13284             :  * @param hAttr Attribute.
   13285             :  * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
   13286             :  *
   13287             :  * @return a buffer of *pnSize bytes.
   13288             :  */
   13289           6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
   13290             : {
   13291           6 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13292           6 :     VALIDATE_POINTER1(pnSize, __func__, nullptr);
   13293          12 :     auto res(hAttr->m_poImpl->ReadAsRaw());
   13294           6 :     *pnSize = res.size();
   13295           6 :     auto ret = res.StealData();
   13296           6 :     if (!ret)
   13297             :     {
   13298           0 :         *pnSize = 0;
   13299           0 :         return nullptr;
   13300             :     }
   13301           6 :     return ret;
   13302             : }
   13303             : 
   13304             : /************************************************************************/
   13305             : /*                       GDALAttributeFreeRawResult()                   */
   13306             : /************************************************************************/
   13307             : 
   13308             : /** Free the return of GDALAttributeAsRaw()
   13309             :  */
   13310           6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
   13311             :                                 CPL_UNUSED size_t nSize)
   13312             : {
   13313           6 :     VALIDATE_POINTER0(hAttr, __func__);
   13314           6 :     if (raw)
   13315             :     {
   13316           6 :         const auto &dt(hAttr->m_poImpl->GetDataType());
   13317           6 :         const auto nDTSize(dt.GetSize());
   13318           6 :         GByte *pabyPtr = raw;
   13319           6 :         const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
   13320           6 :         CPLAssert(nSize == nDTSize * nEltCount);
   13321          12 :         for (size_t i = 0; i < nEltCount; ++i)
   13322             :         {
   13323           6 :             dt.FreeDynamicMemory(pabyPtr);
   13324           6 :             pabyPtr += nDTSize;
   13325             :         }
   13326           6 :         CPLFree(raw);
   13327             :     }
   13328             : }
   13329             : 
   13330             : /************************************************************************/
   13331             : /*                       GDALAttributeReadAsString()                    */
   13332             : /************************************************************************/
   13333             : 
   13334             : /** Return the value of an attribute as a string.
   13335             :  *
   13336             :  * The returned string should not be freed, and its lifetime does not
   13337             :  * excess a next call to ReadAsString() on the same object, or the deletion
   13338             :  * of the object itself.
   13339             :  *
   13340             :  * This function will only return the first element if there are several.
   13341             :  *
   13342             :  * This is the same as the C++ method GDALAttribute::ReadAsString()
   13343             :  *
   13344             :  * @return a string, or nullptr.
   13345             :  */
   13346         107 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
   13347             : {
   13348         107 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13349         107 :     return hAttr->m_poImpl->ReadAsString();
   13350             : }
   13351             : 
   13352             : /************************************************************************/
   13353             : /*                      GDALAttributeReadAsInt()                        */
   13354             : /************************************************************************/
   13355             : 
   13356             : /** Return the value of an attribute as a integer.
   13357             :  *
   13358             :  * This function will only return the first element if there are several.
   13359             :  *
   13360             :  * It can fail if its value can not be converted to integer.
   13361             :  *
   13362             :  * This is the same as the C++ method GDALAttribute::ReadAsInt()
   13363             :  *
   13364             :  * @return a integer, or INT_MIN in case of error.
   13365             :  */
   13366          22 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
   13367             : {
   13368          22 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13369          22 :     return hAttr->m_poImpl->ReadAsInt();
   13370             : }
   13371             : 
   13372             : /************************************************************************/
   13373             : /*                      GDALAttributeReadAsInt64()                      */
   13374             : /************************************************************************/
   13375             : 
   13376             : /** Return the value of an attribute as a int64_t.
   13377             :  *
   13378             :  * This function will only return the first element if there are several.
   13379             :  *
   13380             :  * It can fail if its value can not be converted to integer.
   13381             :  *
   13382             :  * This is the same as the C++ method GDALAttribute::ReadAsInt64()
   13383             :  *
   13384             :  * @return an int64_t, or INT64_MIN in case of error.
   13385             :  */
   13386          15 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
   13387             : {
   13388          15 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13389          15 :     return hAttr->m_poImpl->ReadAsInt64();
   13390             : }
   13391             : 
   13392             : /************************************************************************/
   13393             : /*                       GDALAttributeReadAsDouble()                    */
   13394             : /************************************************************************/
   13395             : 
   13396             : /** Return the value of an attribute as a double.
   13397             :  *
   13398             :  * This function will only return the first element if there are several.
   13399             :  *
   13400             :  * It can fail if its value can not be converted to double.
   13401             :  *
   13402             :  * This is the same as the C++ method GDALAttribute::ReadAsDouble()
   13403             :  *
   13404             :  * @return a double value.
   13405             :  */
   13406          40 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
   13407             : {
   13408          40 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13409          40 :     return hAttr->m_poImpl->ReadAsDouble();
   13410             : }
   13411             : 
   13412             : /************************************************************************/
   13413             : /*                     GDALAttributeReadAsStringArray()                 */
   13414             : /************************************************************************/
   13415             : 
   13416             : /** Return the value of an attribute as an array of strings.
   13417             :  *
   13418             :  * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
   13419             :  *
   13420             :  * The return value must be freed with CSLDestroy().
   13421             :  */
   13422          19 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
   13423             : {
   13424          19 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13425          19 :     return hAttr->m_poImpl->ReadAsStringArray().StealList();
   13426             : }
   13427             : 
   13428             : /************************************************************************/
   13429             : /*                     GDALAttributeReadAsIntArray()                    */
   13430             : /************************************************************************/
   13431             : 
   13432             : /** Return the value of an attribute as an array of integers.
   13433             :  *
   13434             :  * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
   13435             :  *
   13436             :  * @param hAttr Attribute
   13437             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13438             :  * @return array to be freed with CPLFree(), or nullptr.
   13439             :  */
   13440          15 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
   13441             : {
   13442          15 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13443          15 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13444          15 :     *pnCount = 0;
   13445          30 :     auto tmp(hAttr->m_poImpl->ReadAsIntArray());
   13446          15 :     if (tmp.empty())
   13447           0 :         return nullptr;
   13448          15 :     auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
   13449          15 :     if (!ret)
   13450           0 :         return nullptr;
   13451          15 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
   13452          15 :     *pnCount = tmp.size();
   13453          15 :     return ret;
   13454             : }
   13455             : 
   13456             : /************************************************************************/
   13457             : /*                     GDALAttributeReadAsInt64Array()                  */
   13458             : /************************************************************************/
   13459             : 
   13460             : /** Return the value of an attribute as an array of int64_t.
   13461             :  *
   13462             :  * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
   13463             :  *
   13464             :  * @param hAttr Attribute
   13465             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13466             :  * @return array to be freed with CPLFree(), or nullptr.
   13467             :  */
   13468          14 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
   13469             : {
   13470          14 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13471          14 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13472          14 :     *pnCount = 0;
   13473          28 :     auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
   13474          14 :     if (tmp.empty())
   13475           0 :         return nullptr;
   13476             :     auto ret = static_cast<int64_t *>(
   13477          14 :         VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
   13478          14 :     if (!ret)
   13479           0 :         return nullptr;
   13480          14 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
   13481          14 :     *pnCount = tmp.size();
   13482          14 :     return ret;
   13483             : }
   13484             : 
   13485             : /************************************************************************/
   13486             : /*                     GDALAttributeReadAsDoubleArray()                 */
   13487             : /************************************************************************/
   13488             : 
   13489             : /** Return the value of an attribute as an array of doubles.
   13490             :  *
   13491             :  * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
   13492             :  *
   13493             :  * @param hAttr Attribute
   13494             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13495             :  * @return array to be freed with CPLFree(), or nullptr.
   13496             :  */
   13497          29 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
   13498             : {
   13499          29 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13500          29 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13501          29 :     *pnCount = 0;
   13502          58 :     auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
   13503          29 :     if (tmp.empty())
   13504           0 :         return nullptr;
   13505             :     auto ret =
   13506          29 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
   13507          29 :     if (!ret)
   13508           0 :         return nullptr;
   13509          29 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
   13510          29 :     *pnCount = tmp.size();
   13511          29 :     return ret;
   13512             : }
   13513             : 
   13514             : /************************************************************************/
   13515             : /*                     GDALAttributeWriteRaw()                          */
   13516             : /************************************************************************/
   13517             : 
   13518             : /** Write an attribute from raw values expressed in GetDataType()
   13519             :  *
   13520             :  * The values should be provided in the type of GetDataType() and there should
   13521             :  * be exactly GetTotalElementsCount() of them.
   13522             :  * If GetDataType() is a string, each value should be a char* pointer.
   13523             :  *
   13524             :  * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
   13525             :  *
   13526             :  * @param hAttr Attribute
   13527             :  * @param pabyValue Buffer of nLen bytes.
   13528             :  * @param nLength Size of pabyValue in bytes. Should be equal to
   13529             :  *             GetTotalElementsCount() * GetDataType().GetSize()
   13530             :  * @return TRUE in case of success.
   13531             :  */
   13532           5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
   13533             :                           size_t nLength)
   13534             : {
   13535           5 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13536           5 :     return hAttr->m_poImpl->Write(pabyValue, nLength);
   13537             : }
   13538             : 
   13539             : /************************************************************************/
   13540             : /*                     GDALAttributeWriteString()                       */
   13541             : /************************************************************************/
   13542             : 
   13543             : /** Write an attribute from a string value.
   13544             :  *
   13545             :  * Type conversion will be performed if needed. If the attribute contains
   13546             :  * multiple values, only the first one will be updated.
   13547             :  *
   13548             :  * This is the same as the C++ method GDALAttribute::Write(const char*)
   13549             :  *
   13550             :  * @param hAttr Attribute
   13551             :  * @param pszVal Pointer to a string.
   13552             :  * @return TRUE in case of success.
   13553             :  */
   13554         175 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
   13555             : {
   13556         175 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13557         175 :     return hAttr->m_poImpl->Write(pszVal);
   13558             : }
   13559             : 
   13560             : /************************************************************************/
   13561             : /*                        GDALAttributeWriteInt()                       */
   13562             : /************************************************************************/
   13563             : 
   13564             : /** Write an attribute from a integer value.
   13565             :  *
   13566             :  * Type conversion will be performed if needed. If the attribute contains
   13567             :  * multiple values, only the first one will be updated.
   13568             :  *
   13569             :  * This is the same as the C++ method GDALAttribute::WriteInt()
   13570             :  *
   13571             :  * @param hAttr Attribute
   13572             :  * @param nVal Value.
   13573             :  * @return TRUE in case of success.
   13574             :  */
   13575          22 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
   13576             : {
   13577          22 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13578          22 :     return hAttr->m_poImpl->WriteInt(nVal);
   13579             : }
   13580             : 
   13581             : /************************************************************************/
   13582             : /*                        GDALAttributeWriteInt64()                     */
   13583             : /************************************************************************/
   13584             : 
   13585             : /** Write an attribute from an int64_t value.
   13586             :  *
   13587             :  * Type conversion will be performed if needed. If the attribute contains
   13588             :  * multiple values, only the first one will be updated.
   13589             :  *
   13590             :  * This is the same as the C++ method GDALAttribute::WriteLong()
   13591             :  *
   13592             :  * @param hAttr Attribute
   13593             :  * @param nVal Value.
   13594             :  * @return TRUE in case of success.
   13595             :  */
   13596          11 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
   13597             : {
   13598          11 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13599          11 :     return hAttr->m_poImpl->WriteInt64(nVal);
   13600             : }
   13601             : 
   13602             : /************************************************************************/
   13603             : /*                        GDALAttributeWriteDouble()                    */
   13604             : /************************************************************************/
   13605             : 
   13606             : /** Write an attribute from a double value.
   13607             :  *
   13608             :  * Type conversion will be performed if needed. If the attribute contains
   13609             :  * multiple values, only the first one will be updated.
   13610             :  *
   13611             :  * This is the same as the C++ method GDALAttribute::Write(double);
   13612             :  *
   13613             :  * @param hAttr Attribute
   13614             :  * @param dfVal Value.
   13615             :  *
   13616             :  * @return TRUE in case of success.
   13617             :  */
   13618          11 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
   13619             : {
   13620          11 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13621          11 :     return hAttr->m_poImpl->Write(dfVal);
   13622             : }
   13623             : 
   13624             : /************************************************************************/
   13625             : /*                       GDALAttributeWriteStringArray()                */
   13626             : /************************************************************************/
   13627             : 
   13628             : /** Write an attribute from an array of strings.
   13629             :  *
   13630             :  * Type conversion will be performed if needed.
   13631             :  *
   13632             :  * Exactly GetTotalElementsCount() strings must be provided
   13633             :  *
   13634             :  * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
   13635             :  *
   13636             :  * @param hAttr Attribute
   13637             :  * @param papszValues Array of strings.
   13638             :  * @return TRUE in case of success.
   13639             :  */
   13640           8 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
   13641             :                                   CSLConstList papszValues)
   13642             : {
   13643           8 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13644           8 :     return hAttr->m_poImpl->Write(papszValues);
   13645             : }
   13646             : 
   13647             : /************************************************************************/
   13648             : /*                       GDALAttributeWriteIntArray()                */
   13649             : /************************************************************************/
   13650             : 
   13651             : /** Write an attribute from an array of int.
   13652             :  *
   13653             :  * Type conversion will be performed if needed.
   13654             :  *
   13655             :  * Exactly GetTotalElementsCount() strings must be provided
   13656             :  *
   13657             :  * This is the same as the C++ method GDALAttribute::Write(const int *,
   13658             :  * size_t)
   13659             :  *
   13660             :  * @param hAttr Attribute
   13661             :  * @param panValues Array of int.
   13662             :  * @param nCount Should be equal to GetTotalElementsCount().
   13663             :  * @return TRUE in case of success.
   13664             :  */
   13665           9 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
   13666             :                                size_t nCount)
   13667             : {
   13668           9 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13669           9 :     return hAttr->m_poImpl->Write(panValues, nCount);
   13670             : }
   13671             : 
   13672             : /************************************************************************/
   13673             : /*                       GDALAttributeWriteInt64Array()                 */
   13674             : /************************************************************************/
   13675             : 
   13676             : /** Write an attribute from an array of int64_t.
   13677             :  *
   13678             :  * Type conversion will be performed if needed.
   13679             :  *
   13680             :  * Exactly GetTotalElementsCount() strings must be provided
   13681             :  *
   13682             :  * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
   13683             :  * size_t)
   13684             :  *
   13685             :  * @param hAttr Attribute
   13686             :  * @param panValues Array of int64_t.
   13687             :  * @param nCount Should be equal to GetTotalElementsCount().
   13688             :  * @return TRUE in case of success.
   13689             :  */
   13690          10 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
   13691             :                                  size_t nCount)
   13692             : {
   13693          10 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13694          10 :     return hAttr->m_poImpl->Write(panValues, nCount);
   13695             : }
   13696             : 
   13697             : /************************************************************************/
   13698             : /*                       GDALAttributeWriteDoubleArray()                */
   13699             : /************************************************************************/
   13700             : 
   13701             : /** Write an attribute from an array of double.
   13702             :  *
   13703             :  * Type conversion will be performed if needed.
   13704             :  *
   13705             :  * Exactly GetTotalElementsCount() strings must be provided
   13706             :  *
   13707             :  * This is the same as the C++ method GDALAttribute::Write(const double *,
   13708             :  * size_t)
   13709             :  *
   13710             :  * @param hAttr Attribute
   13711             :  * @param padfValues Array of double.
   13712             :  * @param nCount Should be equal to GetTotalElementsCount().
   13713             :  * @return TRUE in case of success.
   13714             :  */
   13715           7 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
   13716             :                                   const double *padfValues, size_t nCount)
   13717             : {
   13718           7 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13719           7 :     return hAttr->m_poImpl->Write(padfValues, nCount);
   13720             : }
   13721             : 
   13722             : /************************************************************************/
   13723             : /*                      GDALAttributeRename()                           */
   13724             : /************************************************************************/
   13725             : 
   13726             : /** Rename the attribute.
   13727             :  *
   13728             :  * This is not implemented by all drivers.
   13729             :  *
   13730             :  * Drivers known to implement it: MEM, netCDF.
   13731             :  *
   13732             :  * This is the same as the C++ method GDALAbstractMDArray::Rename()
   13733             :  *
   13734             :  * @return true in case of success
   13735             :  * @since GDAL 3.8
   13736             :  */
   13737          27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
   13738             : {
   13739          27 :     VALIDATE_POINTER1(hAttr, __func__, false);
   13740          27 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   13741          27 :     return hAttr->m_poImpl->Rename(pszNewName);
   13742             : }
   13743             : 
   13744             : /************************************************************************/
   13745             : /*                        GDALDimensionRelease()                        */
   13746             : /************************************************************************/
   13747             : 
   13748             : /** Release the GDAL in-memory object associated with a GDALDimension.
   13749             :  *
   13750             :  * Note: when applied on a object coming from a driver, this does not
   13751             :  * destroy the object in the file, database, etc...
   13752             :  */
   13753        4908 : void GDALDimensionRelease(GDALDimensionH hDim)
   13754             : {
   13755        4908 :     delete hDim;
   13756        4908 : }
   13757             : 
   13758             : /************************************************************************/
   13759             : /*                        GDALDimensionGetName()                        */
   13760             : /************************************************************************/
   13761             : 
   13762             : /** Return dimension name.
   13763             :  *
   13764             :  * This is the same as the C++ method GDALDimension::GetName()
   13765             :  */
   13766         284 : const char *GDALDimensionGetName(GDALDimensionH hDim)
   13767             : {
   13768         284 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13769         284 :     return hDim->m_poImpl->GetName().c_str();
   13770             : }
   13771             : 
   13772             : /************************************************************************/
   13773             : /*                      GDALDimensionGetFullName()                      */
   13774             : /************************************************************************/
   13775             : 
   13776             : /** Return dimension full name.
   13777             :  *
   13778             :  * This is the same as the C++ method GDALDimension::GetFullName()
   13779             :  */
   13780          80 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
   13781             : {
   13782          80 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13783          80 :     return hDim->m_poImpl->GetFullName().c_str();
   13784             : }
   13785             : 
   13786             : /************************************************************************/
   13787             : /*                        GDALDimensionGetType()                        */
   13788             : /************************************************************************/
   13789             : 
   13790             : /** Return dimension type.
   13791             :  *
   13792             :  * This is the same as the C++ method GDALDimension::GetType()
   13793             :  */
   13794          62 : const char *GDALDimensionGetType(GDALDimensionH hDim)
   13795             : {
   13796          62 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13797          62 :     return hDim->m_poImpl->GetType().c_str();
   13798             : }
   13799             : 
   13800             : /************************************************************************/
   13801             : /*                     GDALDimensionGetDirection()                      */
   13802             : /************************************************************************/
   13803             : 
   13804             : /** Return dimension direction.
   13805             :  *
   13806             :  * This is the same as the C++ method GDALDimension::GetDirection()
   13807             :  */
   13808          32 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
   13809             : {
   13810          32 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13811          32 :     return hDim->m_poImpl->GetDirection().c_str();
   13812             : }
   13813             : 
   13814             : /************************************************************************/
   13815             : /*                        GDALDimensionGetSize()                        */
   13816             : /************************************************************************/
   13817             : 
   13818             : /** Return the size, that is the number of values along the dimension.
   13819             :  *
   13820             :  * This is the same as the C++ method GDALDimension::GetSize()
   13821             :  */
   13822        3681 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
   13823             : {
   13824        3681 :     VALIDATE_POINTER1(hDim, __func__, 0);
   13825        3681 :     return hDim->m_poImpl->GetSize();
   13826             : }
   13827             : 
   13828             : /************************************************************************/
   13829             : /*                     GDALDimensionGetIndexingVariable()               */
   13830             : /************************************************************************/
   13831             : 
   13832             : /** Return the variable that is used to index the dimension (if there is one).
   13833             :  *
   13834             :  * This is the array, typically one-dimensional, describing the values taken
   13835             :  * by the dimension.
   13836             :  *
   13837             :  * The returned value should be freed with GDALMDArrayRelease().
   13838             :  *
   13839             :  * This is the same as the C++ method GDALDimension::GetIndexingVariable()
   13840             :  */
   13841         118 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
   13842             : {
   13843         118 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13844         236 :     auto var(hDim->m_poImpl->GetIndexingVariable());
   13845         118 :     if (!var)
   13846          10 :         return nullptr;
   13847         108 :     return new GDALMDArrayHS(var);
   13848             : }
   13849             : 
   13850             : /************************************************************************/
   13851             : /*                      GDALDimensionSetIndexingVariable()              */
   13852             : /************************************************************************/
   13853             : 
   13854             : /** Set the variable that is used to index the dimension.
   13855             :  *
   13856             :  * This is the array, typically one-dimensional, describing the values taken
   13857             :  * by the dimension.
   13858             :  *
   13859             :  * This is the same as the C++ method GDALDimension::SetIndexingVariable()
   13860             :  *
   13861             :  * @return TRUE in case of success.
   13862             :  */
   13863          23 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
   13864             : {
   13865          23 :     VALIDATE_POINTER1(hDim, __func__, FALSE);
   13866          69 :     return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
   13867          46 :                                                       : nullptr);
   13868             : }
   13869             : 
   13870             : /************************************************************************/
   13871             : /*                      GDALDimensionRename()                           */
   13872             : /************************************************************************/
   13873             : 
   13874             : /** Rename the dimension.
   13875             :  *
   13876             :  * This is not implemented by all drivers.
   13877             :  *
   13878             :  * Drivers known to implement it: MEM, netCDF.
   13879             :  *
   13880             :  * This is the same as the C++ method GDALDimension::Rename()
   13881             :  *
   13882             :  * @return true in case of success
   13883             :  * @since GDAL 3.8
   13884             :  */
   13885          31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
   13886             : {
   13887          31 :     VALIDATE_POINTER1(hDim, __func__, false);
   13888          31 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   13889          31 :     return hDim->m_poImpl->Rename(pszNewName);
   13890             : }
   13891             : 
   13892             : /************************************************************************/
   13893             : /*                       GDALDatasetGetRootGroup()                      */
   13894             : /************************************************************************/
   13895             : 
   13896             : /** Return the root GDALGroup of this dataset.
   13897             :  *
   13898             :  * Only valid for multidimensional datasets.
   13899             :  *
   13900             :  * The returned value must be freed with GDALGroupRelease().
   13901             :  *
   13902             :  * This is the same as the C++ method GDALDataset::GetRootGroup().
   13903             :  *
   13904             :  * @since GDAL 3.1
   13905             :  */
   13906        1176 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
   13907             : {
   13908        1176 :     VALIDATE_POINTER1(hDS, __func__, nullptr);
   13909        1176 :     auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
   13910        1176 :     return poGroup ? new GDALGroupHS(poGroup) : nullptr;
   13911             : }
   13912             : 
   13913             : /************************************************************************/
   13914             : /*                      GDALRasterBandAsMDArray()                        */
   13915             : /************************************************************************/
   13916             : 
   13917             : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
   13918             :  *
   13919             :  * The band must be linked to a GDALDataset. If this dataset is not already
   13920             :  * marked as shared, it will be, so that the returned array holds a reference
   13921             :  * to it.
   13922             :  *
   13923             :  * If the dataset has a geotransform attached, the X and Y dimensions of the
   13924             :  * returned array will have an associated indexing variable.
   13925             :  *
   13926             :  * The returned pointer must be released with GDALMDArrayRelease().
   13927             :  *
   13928             :  * This is the same as the C++ method GDALRasterBand::AsMDArray().
   13929             :  *
   13930             :  * @return a new array, or NULL.
   13931             :  *
   13932             :  * @since GDAL 3.1
   13933             :  */
   13934          21 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
   13935             : {
   13936          21 :     VALIDATE_POINTER1(hBand, __func__, nullptr);
   13937          42 :     auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
   13938          21 :     if (!poArray)
   13939           0 :         return nullptr;
   13940          21 :     return new GDALMDArrayHS(poArray);
   13941             : }
   13942             : 
   13943             : /************************************************************************/
   13944             : /*                       GDALMDArrayAsClassicDataset()                  */
   13945             : /************************************************************************/
   13946             : 
   13947             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   13948             :  *
   13949             :  * Only 2D or more arrays are supported.
   13950             :  *
   13951             :  * In the case of > 2D arrays, additional dimensions will be represented as
   13952             :  * raster bands.
   13953             :  *
   13954             :  * The "reverse" method is GDALRasterBand::AsMDArray().
   13955             :  *
   13956             :  * This is the same as the C++ method GDALMDArray::AsClassicDataset().
   13957             :  *
   13958             :  * @param hArray Array.
   13959             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   13960             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   13961             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   13962             :  */
   13963           0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
   13964             :                                          size_t iYDim)
   13965             : {
   13966           0 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13967           0 :     return GDALDataset::ToHandle(
   13968           0 :         hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
   13969             : }
   13970             : 
   13971             : /************************************************************************/
   13972             : /*                     GDALMDArrayAsClassicDatasetEx()                  */
   13973             : /************************************************************************/
   13974             : 
   13975             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   13976             :  *
   13977             :  * Only 2D or more arrays are supported.
   13978             :  *
   13979             :  * In the case of > 2D arrays, additional dimensions will be represented as
   13980             :  * raster bands.
   13981             :  *
   13982             :  * The "reverse" method is GDALRasterBand::AsMDArray().
   13983             :  *
   13984             :  * This is the same as the C++ method GDALMDArray::AsClassicDataset().
   13985             :  * @param hArray Array.
   13986             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   13987             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   13988             :  *              Ignored if the dimension count is 1.
   13989             :  * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
   13990             :  *                   BAND_IMAGERY_METADATA option.
   13991             :  * @param papszOptions Cf GDALMDArray::AsClassicDataset()
   13992             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   13993             :  * @since GDAL 3.8
   13994             :  */
   13995          71 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
   13996             :                                            size_t iYDim, GDALGroupH hRootGroup,
   13997             :                                            CSLConstList papszOptions)
   13998             : {
   13999          71 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   14000         142 :     return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
   14001         142 :         iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
   14002         142 :         papszOptions));
   14003             : }
   14004             : 
   14005             : //! @cond Doxygen_Suppress
   14006             : 
   14007         180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
   14008             :                                          const std::string &osName,
   14009             :                                          const std::string &osValue,
   14010         180 :                                          GDALExtendedDataTypeSubType eSubType)
   14011             :     : GDALAbstractMDArray(osParentName, osName),
   14012             :       GDALAttribute(osParentName, osName),
   14013         180 :       m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
   14014             : {
   14015         180 : }
   14016             : 
   14017             : const std::vector<std::shared_ptr<GDALDimension>> &
   14018          30 : GDALAttributeString::GetDimensions() const
   14019             : {
   14020          30 :     return m_dims;
   14021             : }
   14022             : 
   14023          21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
   14024             : {
   14025          21 :     return m_dt;
   14026             : }
   14027             : 
   14028          10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
   14029             :                                 const GPtrDiff_t *,
   14030             :                                 const GDALExtendedDataType &bufferDataType,
   14031             :                                 void *pDstBuffer) const
   14032             : {
   14033          10 :     if (bufferDataType.GetClass() != GEDTC_STRING)
   14034           0 :         return false;
   14035          10 :     char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
   14036          10 :     if (!pszStr)
   14037           0 :         return false;
   14038          10 :     memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
   14039          10 :     *static_cast<char **>(pDstBuffer) = pszStr;
   14040          10 :     return true;
   14041             : }
   14042             : 
   14043          66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   14044             :                                            const std::string &osName,
   14045          66 :                                            double dfValue)
   14046             :     : GDALAbstractMDArray(osParentName, osName),
   14047             :       GDALAttribute(osParentName, osName),
   14048          66 :       m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
   14049             : {
   14050          66 : }
   14051             : 
   14052          27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   14053             :                                            const std::string &osName,
   14054          27 :                                            int nValue)
   14055             :     : GDALAbstractMDArray(osParentName, osName),
   14056             :       GDALAttribute(osParentName, osName),
   14057          27 :       m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
   14058             : {
   14059          27 : }
   14060             : 
   14061           7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   14062             :                                            const std::string &osName,
   14063           7 :                                            const std::vector<GUInt32> &anValues)
   14064             :     : GDALAbstractMDArray(osParentName, osName),
   14065             :       GDALAttribute(osParentName, osName),
   14066           7 :       m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
   14067             : {
   14068           7 :     m_dims.push_back(std::make_shared<GDALDimension>(
   14069          14 :         std::string(), "dim0", std::string(), std::string(),
   14070           7 :         m_anValuesUInt32.size()));
   14071           7 : }
   14072             : 
   14073             : const std::vector<std::shared_ptr<GDALDimension>> &
   14074          14 : GDALAttributeNumeric::GetDimensions() const
   14075             : {
   14076          14 :     return m_dims;
   14077             : }
   14078             : 
   14079           8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
   14080             : {
   14081           8 :     return m_dt;
   14082             : }
   14083             : 
   14084           4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
   14085             :                                  const size_t *count, const GInt64 *arrayStep,
   14086             :                                  const GPtrDiff_t *bufferStride,
   14087             :                                  const GDALExtendedDataType &bufferDataType,
   14088             :                                  void *pDstBuffer) const
   14089             : {
   14090           4 :     if (m_dims.empty())
   14091             :     {
   14092           3 :         if (m_dt.GetNumericDataType() == GDT_Float64)
   14093           0 :             GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
   14094             :                                             bufferDataType);
   14095             :         else
   14096             :         {
   14097           3 :             CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
   14098           3 :             GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
   14099             :                                             bufferDataType);
   14100             :         }
   14101             :     }
   14102             :     else
   14103             :     {
   14104           1 :         CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
   14105           1 :         GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
   14106          30 :         for (size_t i = 0; i < count[0]; ++i)
   14107             :         {
   14108          29 :             GDALExtendedDataType::CopyValue(
   14109          29 :                 &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
   14110          29 :                                                       i * arrayStep[0])],
   14111          29 :                 m_dt, pabyDstBuffer, bufferDataType);
   14112          29 :             pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
   14113             :         }
   14114             :     }
   14115           4 :     return true;
   14116             : }
   14117             : 
   14118         194 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
   14119             :     const std::string &osParentName, const std::string &osName,
   14120             :     const std::shared_ptr<GDALDimension> &poDim, double dfStart,
   14121         194 :     double dfIncrement, double dfOffsetInIncrement)
   14122             :     : GDALAbstractMDArray(osParentName, osName),
   14123             :       GDALMDArray(osParentName, osName), m_dfStart(dfStart),
   14124             :       m_dfIncrement(dfIncrement),
   14125         388 :       m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
   14126             : {
   14127         194 : }
   14128             : 
   14129         194 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
   14130             :     const std::string &osParentName, const std::string &osName,
   14131             :     const std::shared_ptr<GDALDimension> &poDim, double dfStart,
   14132             :     double dfIncrement, double dfOffsetInIncrement)
   14133             : {
   14134             :     auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
   14135         194 :         osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
   14136         194 :     poArray->SetSelf(poArray);
   14137         194 :     return poArray;
   14138             : }
   14139             : 
   14140             : const std::vector<std::shared_ptr<GDALDimension>> &
   14141         788 : GDALMDArrayRegularlySpaced::GetDimensions() const
   14142             : {
   14143         788 :     return m_dims;
   14144             : }
   14145             : 
   14146         316 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
   14147             : {
   14148         316 :     return m_dt;
   14149             : }
   14150             : 
   14151             : std::vector<std::shared_ptr<GDALAttribute>>
   14152           4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
   14153             : {
   14154           4 :     return m_attributes;
   14155             : }
   14156             : 
   14157           0 : void GDALMDArrayRegularlySpaced::AddAttribute(
   14158             :     const std::shared_ptr<GDALAttribute> &poAttr)
   14159             : {
   14160           0 :     m_attributes.emplace_back(poAttr);
   14161           0 : }
   14162             : 
   14163         188 : bool GDALMDArrayRegularlySpaced::IRead(
   14164             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
   14165             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
   14166             :     void *pDstBuffer) const
   14167             : {
   14168         188 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
   14169       14719 :     for (size_t i = 0; i < count[0]; i++)
   14170             :     {
   14171       14531 :         const double dfVal = m_dfStart + (arrayStartIdx[0] + i * arrayStep[0] +
   14172       14531 :                                           m_dfOffsetInIncrement) *
   14173       14531 :                                              m_dfIncrement;
   14174       14531 :         GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
   14175             :                                         bufferDataType);
   14176       14531 :         pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
   14177             :     }
   14178         188 :     return true;
   14179             : }
   14180             : 
   14181        3009 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
   14182             :     const std::string &osParentName, const std::string &osName,
   14183        3009 :     const std::string &osType, const std::string &osDirection, GUInt64 nSize)
   14184        3009 :     : GDALDimension(osParentName, osName, osType, osDirection, nSize)
   14185             : {
   14186        3009 : }
   14187             : 
   14188             : std::shared_ptr<GDALMDArray>
   14189         723 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
   14190             : {
   14191         723 :     return m_poIndexingVariable.lock();
   14192             : }
   14193             : 
   14194             : // cppcheck-suppress passedByValue
   14195         500 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
   14196             :     std::shared_ptr<GDALMDArray> poIndexingVariable)
   14197             : {
   14198         500 :     m_poIndexingVariable = poIndexingVariable;
   14199         500 :     return true;
   14200             : }
   14201             : 
   14202          33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
   14203             : {
   14204          33 :     m_nSize = nNewSize;
   14205          33 : }
   14206             : 
   14207             : /************************************************************************/
   14208             : /*                       GDALPamMultiDim::Private                       */
   14209             : /************************************************************************/
   14210             : 
   14211             : struct GDALPamMultiDim::Private
   14212             : {
   14213             :     std::string m_osFilename{};
   14214             :     std::string m_osPamFilename{};
   14215             : 
   14216             :     struct Statistics
   14217             :     {
   14218             :         bool bHasStats = false;
   14219             :         bool bApproxStats = false;
   14220             :         double dfMin = 0;
   14221             :         double dfMax = 0;
   14222             :         double dfMean = 0;
   14223             :         double dfStdDev = 0;
   14224             :         GUInt64 nValidCount = 0;
   14225             :     };
   14226             : 
   14227             :     struct ArrayInfo
   14228             :     {
   14229             :         std::shared_ptr<OGRSpatialReference> poSRS{};
   14230             :         // cppcheck-suppress unusedStructMember
   14231             :         Statistics stats{};
   14232             :     };
   14233             : 
   14234             :     typedef std::pair<std::string, std::string> NameContext;
   14235             :     std::map<NameContext, ArrayInfo> m_oMapArray{};
   14236             :     std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
   14237             :     bool m_bDirty = false;
   14238             :     bool m_bLoaded = false;
   14239             : };
   14240             : 
   14241             : /************************************************************************/
   14242             : /*                          GDALPamMultiDim                             */
   14243             : /************************************************************************/
   14244             : 
   14245        1449 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
   14246        1449 :     : d(new Private())
   14247             : {
   14248        1449 :     d->m_osFilename = osFilename;
   14249        1449 : }
   14250             : 
   14251             : /************************************************************************/
   14252             : /*                   GDALPamMultiDim::~GDALPamMultiDim()                */
   14253             : /************************************************************************/
   14254             : 
   14255        1449 : GDALPamMultiDim::~GDALPamMultiDim()
   14256             : {
   14257        1449 :     if (d->m_bDirty)
   14258          30 :         Save();
   14259        1449 : }
   14260             : 
   14261             : /************************************************************************/
   14262             : /*                          GDALPamMultiDim::Load()                     */
   14263             : /************************************************************************/
   14264             : 
   14265         107 : void GDALPamMultiDim::Load()
   14266             : {
   14267         107 :     if (d->m_bLoaded)
   14268          96 :         return;
   14269          45 :     d->m_bLoaded = true;
   14270             : 
   14271          45 :     const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
   14272          45 :     d->m_osPamFilename =
   14273          90 :         pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
   14274          45 :     CPLXMLTreeCloser oTree(nullptr);
   14275             :     {
   14276          90 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
   14277          45 :         oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
   14278             :     }
   14279          45 :     if (!oTree)
   14280             :     {
   14281          34 :         return;
   14282             :     }
   14283          11 :     const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
   14284          11 :     if (!poPAMMultiDim)
   14285           0 :         return;
   14286          35 :     for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
   14287          24 :          psIter = psIter->psNext)
   14288             :     {
   14289          24 :         if (psIter->eType == CXT_Element &&
   14290          24 :             strcmp(psIter->pszValue, "Array") == 0)
   14291             :         {
   14292          13 :             const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
   14293          13 :             if (!pszName)
   14294           0 :                 continue;
   14295          13 :             const char *pszContext = CPLGetXMLValue(psIter, "context", "");
   14296             :             const auto oKey =
   14297          26 :                 std::pair<std::string, std::string>(pszName, pszContext);
   14298             : 
   14299             :             /* --------------------------------------------------------------------
   14300             :              */
   14301             :             /*      Check for an SRS node. */
   14302             :             /* --------------------------------------------------------------------
   14303             :              */
   14304          13 :             const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
   14305          13 :             if (psSRSNode)
   14306             :             {
   14307             :                 std::shared_ptr<OGRSpatialReference> poSRS =
   14308           6 :                     std::make_shared<OGRSpatialReference>();
   14309           3 :                 poSRS->SetFromUserInput(
   14310             :                     CPLGetXMLValue(psSRSNode, nullptr, ""),
   14311             :                     OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
   14312           3 :                 const char *pszMapping = CPLGetXMLValue(
   14313             :                     psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
   14314           3 :                 if (pszMapping)
   14315             :                 {
   14316             :                     char **papszTokens =
   14317           3 :                         CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
   14318           6 :                     std::vector<int> anMapping;
   14319           9 :                     for (int i = 0; papszTokens && papszTokens[i]; i++)
   14320             :                     {
   14321           6 :                         anMapping.push_back(atoi(papszTokens[i]));
   14322             :                     }
   14323           3 :                     CSLDestroy(papszTokens);
   14324           3 :                     poSRS->SetDataAxisToSRSAxisMapping(anMapping);
   14325             :                 }
   14326             :                 else
   14327             :                 {
   14328           0 :                     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
   14329             :                 }
   14330             : 
   14331             :                 const char *pszCoordinateEpoch =
   14332           3 :                     CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
   14333           3 :                 if (pszCoordinateEpoch)
   14334           3 :                     poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
   14335             : 
   14336           3 :                 d->m_oMapArray[oKey].poSRS = std::move(poSRS);
   14337             :             }
   14338             : 
   14339             :             const CPLXMLNode *psStatistics =
   14340          13 :                 CPLGetXMLNode(psIter, "Statistics");
   14341          13 :             if (psStatistics)
   14342             :             {
   14343           7 :                 Private::Statistics sStats;
   14344           7 :                 sStats.bHasStats = true;
   14345           7 :                 sStats.bApproxStats = CPLTestBool(
   14346             :                     CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
   14347           7 :                 sStats.dfMin =
   14348           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
   14349           7 :                 sStats.dfMax =
   14350           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
   14351           7 :                 sStats.dfMean =
   14352           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
   14353           7 :                 sStats.dfStdDev =
   14354           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
   14355           7 :                 sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
   14356             :                     CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
   14357           7 :                 d->m_oMapArray[oKey].stats = sStats;
   14358          13 :             }
   14359             :         }
   14360             :         else
   14361             :         {
   14362          11 :             CPLXMLNode *psNextBackup = psIter->psNext;
   14363          11 :             psIter->psNext = nullptr;
   14364          11 :             d->m_apoOtherNodes.emplace_back(
   14365          11 :                 CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
   14366          11 :             psIter->psNext = psNextBackup;
   14367             :         }
   14368             :     }
   14369             : }
   14370             : 
   14371             : /************************************************************************/
   14372             : /*                          GDALPamMultiDim::Save()                     */
   14373             : /************************************************************************/
   14374             : 
   14375          30 : void GDALPamMultiDim::Save()
   14376             : {
   14377             :     CPLXMLTreeCloser oTree(
   14378          60 :         CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
   14379          34 :     for (const auto &poOtherNode : d->m_apoOtherNodes)
   14380             :     {
   14381           4 :         CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
   14382             :     }
   14383         112 :     for (const auto &kv : d->m_oMapArray)
   14384             :     {
   14385             :         CPLXMLNode *psArrayNode =
   14386          82 :             CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
   14387          82 :         CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
   14388          82 :         if (!kv.first.second.empty())
   14389             :         {
   14390           1 :             CPLAddXMLAttributeAndValue(psArrayNode, "context",
   14391             :                                        kv.first.second.c_str());
   14392             :         }
   14393          82 :         if (kv.second.poSRS)
   14394             :         {
   14395          71 :             char *pszWKT = nullptr;
   14396             :             {
   14397         142 :                 CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
   14398          71 :                 const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
   14399          71 :                 kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
   14400             :             }
   14401             :             CPLXMLNode *psSRSNode =
   14402          71 :                 CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
   14403          71 :             CPLFree(pszWKT);
   14404             :             const auto &mapping =
   14405          71 :                 kv.second.poSRS->GetDataAxisToSRSAxisMapping();
   14406         142 :             CPLString osMapping;
   14407         213 :             for (size_t i = 0; i < mapping.size(); ++i)
   14408             :             {
   14409         142 :                 if (!osMapping.empty())
   14410          71 :                     osMapping += ",";
   14411         142 :                 osMapping += CPLSPrintf("%d", mapping[i]);
   14412             :             }
   14413          71 :             CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
   14414             :                                        osMapping.c_str());
   14415             : 
   14416             :             const double dfCoordinateEpoch =
   14417          71 :                 kv.second.poSRS->GetCoordinateEpoch();
   14418          71 :             if (dfCoordinateEpoch > 0)
   14419             :             {
   14420             :                 std::string osCoordinateEpoch =
   14421           2 :                     CPLSPrintf("%f", dfCoordinateEpoch);
   14422           1 :                 if (osCoordinateEpoch.find('.') != std::string::npos)
   14423             :                 {
   14424           6 :                     while (osCoordinateEpoch.back() == '0')
   14425           5 :                         osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
   14426             :                 }
   14427           1 :                 CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
   14428             :                                            osCoordinateEpoch.c_str());
   14429             :             }
   14430             :         }
   14431             : 
   14432          82 :         if (kv.second.stats.bHasStats)
   14433             :         {
   14434             :             CPLXMLNode *psMDArray =
   14435           8 :                 CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
   14436           8 :             CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
   14437           8 :                                         kv.second.stats.bApproxStats ? "1"
   14438             :                                                                      : "0");
   14439           8 :             CPLCreateXMLElementAndValue(
   14440             :                 psMDArray, "Minimum",
   14441           8 :                 CPLSPrintf("%.17g", kv.second.stats.dfMin));
   14442           8 :             CPLCreateXMLElementAndValue(
   14443             :                 psMDArray, "Maximum",
   14444           8 :                 CPLSPrintf("%.17g", kv.second.stats.dfMax));
   14445           8 :             CPLCreateXMLElementAndValue(
   14446           8 :                 psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
   14447           8 :             CPLCreateXMLElementAndValue(
   14448             :                 psMDArray, "StdDev",
   14449           8 :                 CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
   14450           8 :             CPLCreateXMLElementAndValue(
   14451             :                 psMDArray, "ValidSampleCount",
   14452           8 :                 CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
   14453             :         }
   14454             :     }
   14455             : 
   14456             :     int bSaved;
   14457          60 :     CPLErrorAccumulator oErrorAccumulator;
   14458             :     {
   14459          30 :         auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
   14460          30 :         CPL_IGNORE_RET_VAL(oAccumulator);
   14461             :         bSaved =
   14462          30 :             CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
   14463             :     }
   14464             : 
   14465          30 :     const char *pszNewPam = nullptr;
   14466          30 :     if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
   14467           0 :         ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
   14468             :     {
   14469           0 :         CPLErrorReset();
   14470           0 :         CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
   14471             :     }
   14472             :     else
   14473             :     {
   14474          30 :         oErrorAccumulator.ReplayErrors();
   14475             :     }
   14476          30 : }
   14477             : 
   14478             : /************************************************************************/
   14479             : /*                    GDALPamMultiDim::GetSpatialRef()                  */
   14480             : /************************************************************************/
   14481             : 
   14482             : std::shared_ptr<OGRSpatialReference>
   14483          10 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
   14484             :                                const std::string &osContext)
   14485             : {
   14486          10 :     Load();
   14487             :     auto oIter =
   14488          10 :         d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
   14489          10 :     if (oIter != d->m_oMapArray.end())
   14490           2 :         return oIter->second.poSRS;
   14491           8 :     return nullptr;
   14492             : }
   14493             : 
   14494             : /************************************************************************/
   14495             : /*                    GDALPamMultiDim::SetSpatialRef()                  */
   14496             : /************************************************************************/
   14497             : 
   14498          72 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
   14499             :                                     const std::string &osContext,
   14500             :                                     const OGRSpatialReference *poSRS)
   14501             : {
   14502          72 :     Load();
   14503          72 :     d->m_bDirty = true;
   14504          72 :     if (poSRS && !poSRS->IsEmpty())
   14505          71 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
   14506             :             poSRS->Clone());
   14507             :     else
   14508           2 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
   14509           1 :             .poSRS.reset();
   14510          72 : }
   14511             : 
   14512             : /************************************************************************/
   14513             : /*                           GetStatistics()                            */
   14514             : /************************************************************************/
   14515             : 
   14516          16 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
   14517             :                                       const std::string &osContext,
   14518             :                                       bool bApproxOK, double *pdfMin,
   14519             :                                       double *pdfMax, double *pdfMean,
   14520             :                                       double *pdfStdDev, GUInt64 *pnValidCount)
   14521             : {
   14522          16 :     Load();
   14523             :     auto oIter =
   14524          16 :         d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
   14525          16 :     if (oIter == d->m_oMapArray.end())
   14526           9 :         return CE_Failure;
   14527           7 :     const auto &stats = oIter->second.stats;
   14528           7 :     if (!stats.bHasStats)
   14529           1 :         return CE_Failure;
   14530           6 :     if (!bApproxOK && stats.bApproxStats)
   14531           0 :         return CE_Failure;
   14532           6 :     if (pdfMin)
   14533           6 :         *pdfMin = stats.dfMin;
   14534           6 :     if (pdfMax)
   14535           6 :         *pdfMax = stats.dfMax;
   14536           6 :     if (pdfMean)
   14537           6 :         *pdfMean = stats.dfMean;
   14538           6 :     if (pdfStdDev)
   14539           6 :         *pdfStdDev = stats.dfStdDev;
   14540           6 :     if (pnValidCount)
   14541           6 :         *pnValidCount = stats.nValidCount;
   14542           6 :     return CE_None;
   14543             : }
   14544             : 
   14545             : /************************************************************************/
   14546             : /*                           SetStatistics()                            */
   14547             : /************************************************************************/
   14548             : 
   14549           8 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
   14550             :                                     const std::string &osContext,
   14551             :                                     bool bApproxStats, double dfMin,
   14552             :                                     double dfMax, double dfMean,
   14553             :                                     double dfStdDev, GUInt64 nValidCount)
   14554             : {
   14555           8 :     Load();
   14556           8 :     d->m_bDirty = true;
   14557             :     auto &stats =
   14558           8 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
   14559           8 :     stats.bHasStats = true;
   14560           8 :     stats.bApproxStats = bApproxStats;
   14561           8 :     stats.dfMin = dfMin;
   14562           8 :     stats.dfMax = dfMax;
   14563           8 :     stats.dfMean = dfMean;
   14564           8 :     stats.dfStdDev = dfStdDev;
   14565           8 :     stats.nValidCount = nValidCount;
   14566           8 : }
   14567             : 
   14568             : /************************************************************************/
   14569             : /*                           ClearStatistics()                          */
   14570             : /************************************************************************/
   14571             : 
   14572           0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
   14573             :                                       const std::string &osContext)
   14574             : {
   14575           0 :     Load();
   14576           0 :     d->m_bDirty = true;
   14577           0 :     d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
   14578             :         false;
   14579           0 : }
   14580             : 
   14581             : /************************************************************************/
   14582             : /*                           ClearStatistics()                          */
   14583             : /************************************************************************/
   14584             : 
   14585           1 : void GDALPamMultiDim::ClearStatistics()
   14586             : {
   14587           1 :     Load();
   14588           1 :     d->m_bDirty = true;
   14589           3 :     for (auto &kv : d->m_oMapArray)
   14590           2 :         kv.second.stats.bHasStats = false;
   14591           1 : }
   14592             : 
   14593             : /************************************************************************/
   14594             : /*                             GetPAM()                                 */
   14595             : /************************************************************************/
   14596             : 
   14597             : /*static*/ std::shared_ptr<GDALPamMultiDim>
   14598         815 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
   14599             : {
   14600         815 :     auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
   14601         815 :     if (poPamArray)
   14602         567 :         return poPamArray->GetPAM();
   14603         248 :     return nullptr;
   14604             : }
   14605             : 
   14606             : /************************************************************************/
   14607             : /*                           GDALPamMDArray                             */
   14608             : /************************************************************************/
   14609             : 
   14610        3755 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
   14611             :                                const std::string &osName,
   14612             :                                const std::shared_ptr<GDALPamMultiDim> &poPam,
   14613           0 :                                const std::string &osContext)
   14614             :     :
   14615             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
   14616             :       GDALAbstractMDArray(osParentName, osName),
   14617             : #endif
   14618        3755 :       GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
   14619             : {
   14620        3755 : }
   14621             : 
   14622             : /************************************************************************/
   14623             : /*                    GDALPamMDArray::SetSpatialRef()                   */
   14624             : /************************************************************************/
   14625             : 
   14626          72 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
   14627             : {
   14628          72 :     if (!m_poPam)
   14629           0 :         return false;
   14630          72 :     m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
   14631          72 :     return true;
   14632             : }
   14633             : 
   14634             : /************************************************************************/
   14635             : /*                    GDALPamMDArray::GetSpatialRef()                   */
   14636             : /************************************************************************/
   14637             : 
   14638          10 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
   14639             : {
   14640          10 :     if (!m_poPam)
   14641           0 :         return nullptr;
   14642          10 :     return m_poPam->GetSpatialRef(GetFullName(), GetContext());
   14643             : }
   14644             : 
   14645             : /************************************************************************/
   14646             : /*                           GetStatistics()                            */
   14647             : /************************************************************************/
   14648             : 
   14649          16 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
   14650             :                                      double *pdfMin, double *pdfMax,
   14651             :                                      double *pdfMean, double *pdfStdDev,
   14652             :                                      GUInt64 *pnValidCount,
   14653             :                                      GDALProgressFunc pfnProgress,
   14654             :                                      void *pProgressData)
   14655             : {
   14656          16 :     if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
   14657             :                                           bApproxOK, pdfMin, pdfMax, pdfMean,
   14658          16 :                                           pdfStdDev, pnValidCount) == CE_None)
   14659             :     {
   14660           6 :         return CE_None;
   14661             :     }
   14662          10 :     if (!bForce)
   14663           4 :         return CE_Warning;
   14664             : 
   14665           6 :     return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
   14666             :                                       pdfMean, pdfStdDev, pnValidCount,
   14667           6 :                                       pfnProgress, pProgressData);
   14668             : }
   14669             : 
   14670             : /************************************************************************/
   14671             : /*                           SetStatistics()                            */
   14672             : /************************************************************************/
   14673             : 
   14674           8 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
   14675             :                                    double dfMax, double dfMean, double dfStdDev,
   14676             :                                    GUInt64 nValidCount,
   14677             :                                    CSLConstList /* papszOptions */)
   14678             : {
   14679           8 :     if (!m_poPam)
   14680           0 :         return false;
   14681           8 :     m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
   14682             :                            dfMax, dfMean, dfStdDev, nValidCount);
   14683           8 :     return true;
   14684             : }
   14685             : 
   14686             : /************************************************************************/
   14687             : /*                           ClearStatistics()                          */
   14688             : /************************************************************************/
   14689             : 
   14690           0 : void GDALPamMDArray::ClearStatistics()
   14691             : {
   14692           0 :     if (!m_poPam)
   14693           0 :         return;
   14694           0 :     m_poPam->ClearStatistics(GetFullName(), GetContext());
   14695             : }
   14696             : 
   14697             : //! @endcond

Generated by: LCOV version 1.14