LCOV - code coverage report
Current view: top level - gcore - gdalmultidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 5114 5629 90.9 %
Date: 2026-03-13 03:16:58 Functions: 493 568 86.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Name:     gdalmultidim.cpp
       4             :  * Project:  GDAL Core
       5             :  * Purpose:  GDAL Core C++/Private implementation for multidimensional support
       6             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include <assert.h>
      15             : #include <algorithm>
      16             : #include <limits>
      17             : #include <list>
      18             : #include <queue>
      19             : #include <set>
      20             : #include <utility>
      21             : #include <time.h>
      22             : 
      23             : #include <cmath>
      24             : #include <ctype.h>  // isalnum
      25             : 
      26             : #include "cpl_error_internal.h"
      27             : #include "cpl_float.h"
      28             : #include "gdal_priv.h"
      29             : #include "gdal_pam.h"
      30             : #include "gdal_pam_multidim.h"
      31             : #include "gdal_rat.h"
      32             : #include "gdal_utils.h"
      33             : #include "cpl_safemaths.hpp"
      34             : #include "memmultidim.h"
      35             : #include "ogrsf_frmts.h"
      36             : #include "gdalmultidim_priv.h"
      37             : 
      38             : #if defined(__clang__) || defined(_MSC_VER)
      39             : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
      40             : #endif
      41             : 
      42             : /************************************************************************/
      43             : /*                         GDALMDArrayUnscaled                          */
      44             : /************************************************************************/
      45             : 
      46             : class GDALMDArrayUnscaled final : public GDALPamMDArray
      47             : {
      48             :   private:
      49             :     std::shared_ptr<GDALMDArray> m_poParent{};
      50             :     const GDALExtendedDataType m_dt;
      51             :     bool m_bHasNoData;
      52             :     const double m_dfScale;
      53             :     const double m_dfOffset;
      54             :     std::vector<GByte> m_abyRawNoData{};
      55             : 
      56             :   protected:
      57          13 :     explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
      58             :                                  double dfScale, double dfOffset,
      59             :                                  double dfOverriddenDstNodata, GDALDataType eDT)
      60          26 :         : GDALAbstractMDArray(std::string(),
      61          26 :                               "Unscaled view of " + poParent->GetFullName()),
      62             :           GDALPamMDArray(
      63          26 :               std::string(), "Unscaled view of " + poParent->GetFullName(),
      64          26 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
      65          13 :           m_poParent(std::move(poParent)),
      66             :           m_dt(GDALExtendedDataType::Create(eDT)),
      67          13 :           m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
      68          78 :           m_dfScale(dfScale), m_dfOffset(dfOffset)
      69             :     {
      70          13 :         m_abyRawNoData.resize(m_dt.GetSize());
      71             :         const auto eNonComplexDT =
      72          13 :             GDALGetNonComplexDataType(m_dt.GetNumericDataType());
      73          26 :         GDALCopyWords64(
      74          13 :             &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
      75             :             eNonComplexDT, GDALGetDataTypeSizeBytes(eNonComplexDT),
      76          13 :             GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
      77          13 :     }
      78             : 
      79             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
      80             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      81             :                const GDALExtendedDataType &bufferDataType,
      82             :                void *pDstBuffer) const override;
      83             : 
      84             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
      85             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      86             :                 const GDALExtendedDataType &bufferDataType,
      87             :                 const void *pSrcBuffer) override;
      88             : 
      89           0 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
      90             :                      CSLConstList papszOptions) const override
      91             :     {
      92           0 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
      93             :     }
      94             : 
      95             :   public:
      96             :     static std::shared_ptr<GDALMDArrayUnscaled>
      97          13 :     Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
      98             :            double dfOffset, double dfDstNodata, GDALDataType eDT)
      99             :     {
     100             :         auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
     101          13 :             poParent, dfScale, dfOffset, dfDstNodata, eDT)));
     102          13 :         newAr->SetSelf(newAr);
     103          13 :         return newAr;
     104             :     }
     105             : 
     106           1 :     bool IsWritable() const override
     107             :     {
     108           1 :         return m_poParent->IsWritable();
     109             :     }
     110             : 
     111          15 :     const std::string &GetFilename() const override
     112             :     {
     113          15 :         return m_poParent->GetFilename();
     114             :     }
     115             : 
     116             :     const std::vector<std::shared_ptr<GDALDimension>> &
     117         220 :     GetDimensions() const override
     118             :     {
     119         220 :         return m_poParent->GetDimensions();
     120             :     }
     121             : 
     122         103 :     const GDALExtendedDataType &GetDataType() const override
     123             :     {
     124         103 :         return m_dt;
     125             :     }
     126             : 
     127           1 :     const std::string &GetUnit() const override
     128             :     {
     129           1 :         return m_poParent->GetUnit();
     130             :     }
     131             : 
     132           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
     133             :     {
     134           1 :         return m_poParent->GetSpatialRef();
     135             :     }
     136             : 
     137           6 :     const void *GetRawNoDataValue() const override
     138             :     {
     139           6 :         return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
     140             :     }
     141             : 
     142           1 :     bool SetRawNoDataValue(const void *pRawNoData) override
     143             :     {
     144           1 :         m_bHasNoData = true;
     145           1 :         memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
     146           1 :         return true;
     147             :     }
     148             : 
     149           4 :     std::vector<GUInt64> GetBlockSize() const override
     150             :     {
     151           4 :         return m_poParent->GetBlockSize();
     152             :     }
     153             : 
     154             :     std::shared_ptr<GDALAttribute>
     155           0 :     GetAttribute(const std::string &osName) const override
     156             :     {
     157           0 :         return m_poParent->GetAttribute(osName);
     158             :     }
     159             : 
     160             :     std::vector<std::shared_ptr<GDALAttribute>>
     161           1 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
     162             :     {
     163           1 :         return m_poParent->GetAttributes(papszOptions);
     164             :     }
     165             : 
     166           0 :     bool SetUnit(const std::string &osUnit) override
     167             :     {
     168           0 :         return m_poParent->SetUnit(osUnit);
     169             :     }
     170             : 
     171           0 :     bool SetSpatialRef(const OGRSpatialReference *poSRS) override
     172             :     {
     173           0 :         return m_poParent->SetSpatialRef(poSRS);
     174             :     }
     175             : 
     176             :     std::shared_ptr<GDALAttribute>
     177           1 :     CreateAttribute(const std::string &osName,
     178             :                     const std::vector<GUInt64> &anDimensions,
     179             :                     const GDALExtendedDataType &oDataType,
     180             :                     CSLConstList papszOptions = nullptr) override
     181             :     {
     182           1 :         return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
     183           1 :                                            papszOptions);
     184             :     }
     185             : };
     186             : 
     187             : /************************************************************************/
     188             : /*                         ~GDALIHasAttribute()                         */
     189             : /************************************************************************/
     190             : 
     191             : GDALIHasAttribute::~GDALIHasAttribute() = default;
     192             : 
     193             : /************************************************************************/
     194             : /*                            GetAttribute()                            */
     195             : /************************************************************************/
     196             : 
     197             : /** Return an attribute by its name.
     198             :  *
     199             :  * If the attribute does not exist, nullptr should be silently returned.
     200             :  *
     201             :  * @note Driver implementation: this method will fallback to
     202             :  * GetAttributeFromAttributes() is not explicitly implemented
     203             :  *
     204             :  * Drivers known to implement it for groups and arrays: MEM, netCDF.
     205             :  *
     206             :  * This is the same as the C function GDALGroupGetAttribute() or
     207             :  * GDALMDArrayGetAttribute().
     208             :  *
     209             :  * @param osName Attribute name
     210             :  * @return the attribute, or nullptr if it does not exist or an error occurred.
     211             :  */
     212             : std::shared_ptr<GDALAttribute>
     213       10737 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
     214             : {
     215       10737 :     return GetAttributeFromAttributes(osName);
     216             : }
     217             : 
     218             : /************************************************************************/
     219             : /*                     GetAttributeFromAttributes()                     */
     220             : /************************************************************************/
     221             : 
     222             : /** Possible fallback implementation for GetAttribute() using GetAttributes().
     223             :  */
     224             : std::shared_ptr<GDALAttribute>
     225       10737 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
     226             : {
     227       21474 :     auto attrs(GetAttributes());
     228       85091 :     for (const auto &attr : attrs)
     229             :     {
     230       83301 :         if (attr->GetName() == osName)
     231        8947 :             return attr;
     232             :     }
     233        1790 :     return nullptr;
     234             : }
     235             : 
     236             : /************************************************************************/
     237             : /*                           GetAttributes()                            */
     238             : /************************************************************************/
     239             : 
     240             : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
     241             :  *
     242             :  * If the attribute does not exist, nullptr should be silently returned.
     243             :  *
     244             :  * @note Driver implementation: optionally implemented. If implemented,
     245             :  * GetAttribute() should also be implemented.
     246             :  *
     247             :  * Drivers known to implement it for groups and arrays: MEM, netCDF.
     248             :  *
     249             :  * This is the same as the C function GDALGroupGetAttributes() or
     250             :  * GDALMDArrayGetAttributes().
     251             : 
     252             :  * @param papszOptions Driver specific options determining how attributes
     253             :  * should be retrieved. Pass nullptr for default behavior.
     254             :  *
     255             :  * @return the attributes.
     256             :  */
     257             : std::vector<std::shared_ptr<GDALAttribute>>
     258         203 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
     259             : {
     260         203 :     return {};
     261             : }
     262             : 
     263             : /************************************************************************/
     264             : /*                          CreateAttribute()                           */
     265             : /************************************************************************/
     266             : 
     267             : /** Create an attribute within a GDALMDArray or GDALGroup.
     268             :  *
     269             :  * The attribute might not be "physically" created until a value is written
     270             :  * into it.
     271             :  *
     272             :  * Optionally implemented.
     273             :  *
     274             :  * Drivers known to implement it: MEM, netCDF
     275             :  *
     276             :  * This is the same as the C function GDALGroupCreateAttribute() or
     277             :  * GDALMDArrayCreateAttribute()
     278             :  *
     279             :  * @param osName Attribute name.
     280             :  * @param anDimensions List of dimension sizes, ordered from the slowest varying
     281             :  *                     dimension first to the fastest varying dimension last.
     282             :  *                     Empty for a scalar attribute (common case)
     283             :  * @param oDataType  Attribute data type.
     284             :  * @param papszOptions Driver specific options determining how the attribute.
     285             :  * should be created.
     286             :  *
     287             :  * @return the new attribute, or nullptr if case of error
     288             :  */
     289           0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
     290             :     CPL_UNUSED const std::string &osName,
     291             :     CPL_UNUSED const std::vector<GUInt64> &anDimensions,
     292             :     CPL_UNUSED const GDALExtendedDataType &oDataType,
     293             :     CPL_UNUSED CSLConstList papszOptions)
     294             : {
     295           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     296             :              "CreateAttribute() not implemented");
     297           0 :     return nullptr;
     298             : }
     299             : 
     300             : /************************************************************************/
     301             : /*                          DeleteAttribute()                           */
     302             : /************************************************************************/
     303             : 
     304             : /** Delete an attribute from a GDALMDArray or GDALGroup.
     305             :  *
     306             :  * Optionally implemented.
     307             :  *
     308             :  * After this call, if a previously obtained instance of the deleted object
     309             :  * is still alive, no method other than for freeing it should be invoked.
     310             :  *
     311             :  * Drivers known to implement it: MEM, netCDF
     312             :  *
     313             :  * This is the same as the C function GDALGroupDeleteAttribute() or
     314             :  * GDALMDArrayDeleteAttribute()
     315             :  *
     316             :  * @param osName Attribute name.
     317             :  * @param papszOptions Driver specific options determining how the attribute.
     318             :  * should be deleted.
     319             :  *
     320             :  * @return true in case of success
     321             :  * @since GDAL 3.8
     322             :  */
     323           0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
     324             :                                         CPL_UNUSED CSLConstList papszOptions)
     325             : {
     326           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     327             :              "DeleteAttribute() not implemented");
     328           0 :     return false;
     329             : }
     330             : 
     331             : /************************************************************************/
     332             : /*                             GDALGroup()                              */
     333             : /************************************************************************/
     334             : 
     335             : //! @cond Doxygen_Suppress
     336       13184 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
     337       13184 :                      const std::string &osContext)
     338       13184 :     : m_osName(osParentName.empty() ? "/" : osName),
     339             :       m_osFullName(
     340       26368 :           !osParentName.empty()
     341       20019 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
     342             :               : "/"),
     343       33203 :       m_osContext(osContext)
     344             : {
     345       13184 : }
     346             : 
     347             : //! @endcond
     348             : 
     349             : /************************************************************************/
     350             : /*                             ~GDALGroup()                             */
     351             : /************************************************************************/
     352             : 
     353             : GDALGroup::~GDALGroup() = default;
     354             : 
     355             : /************************************************************************/
     356             : /*                          GetMDArrayNames()                           */
     357             : /************************************************************************/
     358             : 
     359             : /** Return the list of multidimensional array names contained in this group.
     360             :  *
     361             :  * @note Driver implementation: optionally implemented. If implemented,
     362             :  * OpenMDArray() should also be implemented.
     363             :  *
     364             :  * Drivers known to implement it: MEM, netCDF.
     365             :  *
     366             :  * This is the same as the C function GDALGroupGetMDArrayNames().
     367             :  *
     368             :  * @param papszOptions Driver specific options determining how arrays
     369             :  * should be retrieved. Pass nullptr for default behavior.
     370             :  *
     371             :  * @return the array names.
     372             :  */
     373             : std::vector<std::string>
     374           0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
     375             : {
     376           0 :     return {};
     377             : }
     378             : 
     379             : /************************************************************************/
     380             : /*                    GetMDArrayFullNamesRecursive()                    */
     381             : /************************************************************************/
     382             : 
     383             : /** Return the list of multidimensional array full names contained in this
     384             :  * group and its subgroups.
     385             :  *
     386             :  * This is the same as the C function GDALGroupGetMDArrayFullNamesRecursive().
     387             :  *
     388             :  * @param papszGroupOptions Driver specific options determining how groups
     389             :  * should be retrieved. Pass nullptr for default behavior.
     390             :  * @param papszArrayOptions Driver specific options determining how arrays
     391             :  * should be retrieved. Pass nullptr for default behavior.
     392             :  *
     393             :  * @return the array full names.
     394             :  *
     395             :  * @since 3.11
     396             :  */
     397             : std::vector<std::string>
     398          10 : GDALGroup::GetMDArrayFullNamesRecursive(CSLConstList papszGroupOptions,
     399             :                                         CSLConstList papszArrayOptions) const
     400             : {
     401          10 :     std::vector<std::string> ret;
     402          20 :     std::list<std::shared_ptr<GDALGroup>> stackGroups;
     403          10 :     stackGroups.push_back(nullptr);  // nullptr means this
     404          23 :     while (!stackGroups.empty())
     405             :     {
     406          26 :         std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
     407          13 :         stackGroups.erase(stackGroups.begin());
     408          13 :         const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
     409          31 :         for (const std::string &arrayName :
     410          75 :              poCurGroup->GetMDArrayNames(papszArrayOptions))
     411             :         {
     412          62 :             std::string osFullName = poCurGroup->GetFullName();
     413          31 :             if (!osFullName.empty() && osFullName.back() != '/')
     414           3 :                 osFullName += '/';
     415          31 :             osFullName += arrayName;
     416          31 :             ret.push_back(std::move(osFullName));
     417             :         }
     418          13 :         auto insertionPoint = stackGroups.begin();
     419           3 :         for (const auto &osSubGroup :
     420          19 :              poCurGroup->GetGroupNames(papszGroupOptions))
     421             :         {
     422           6 :             auto poSubGroup = poCurGroup->OpenGroup(osSubGroup);
     423           3 :             if (poSubGroup)
     424           3 :                 stackGroups.insert(insertionPoint, std::move(poSubGroup));
     425             :         }
     426             :     }
     427             : 
     428          20 :     return ret;
     429             : }
     430             : 
     431             : /************************************************************************/
     432             : /*                            OpenMDArray()                             */
     433             : /************************************************************************/
     434             : 
     435             : /** Open and return a multidimensional array.
     436             :  *
     437             :  * @note Driver implementation: optionally implemented. If implemented,
     438             :  * GetMDArrayNames() should also be implemented.
     439             :  *
     440             :  * Drivers known to implement it: MEM, netCDF.
     441             :  *
     442             :  * This is the same as the C function GDALGroupOpenMDArray().
     443             :  *
     444             :  * @param osName Array name.
     445             :  * @param papszOptions Driver specific options determining how the array should
     446             :  * be opened.  Pass nullptr for default behavior.
     447             :  *
     448             :  * @return the array, or nullptr.
     449             :  */
     450             : std::shared_ptr<GDALMDArray>
     451           0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
     452             :                        CPL_UNUSED CSLConstList papszOptions) const
     453             : {
     454           0 :     return nullptr;
     455             : }
     456             : 
     457             : /************************************************************************/
     458             : /*                           GetGroupNames()                            */
     459             : /************************************************************************/
     460             : 
     461             : /** Return the list of sub-groups contained in this group.
     462             :  *
     463             :  * @note Driver implementation: optionally implemented. If implemented,
     464             :  * OpenGroup() should also be implemented.
     465             :  *
     466             :  * Drivers known to implement it: MEM, netCDF.
     467             :  *
     468             :  * This is the same as the C function GDALGroupGetGroupNames().
     469             :  *
     470             :  * @param papszOptions Driver specific options determining how groups
     471             :  * should be retrieved. Pass nullptr for default behavior.
     472             :  *
     473             :  * @return the group names.
     474             :  */
     475             : std::vector<std::string>
     476           4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
     477             : {
     478           4 :     return {};
     479             : }
     480             : 
     481             : /************************************************************************/
     482             : /*                             OpenGroup()                              */
     483             : /************************************************************************/
     484             : 
     485             : /** Open and return a sub-group.
     486             :  *
     487             :  * @note Driver implementation: optionally implemented. If implemented,
     488             :  * GetGroupNames() should also be implemented.
     489             :  *
     490             :  * Drivers known to implement it: MEM, netCDF.
     491             :  *
     492             :  * This is the same as the C function GDALGroupOpenGroup().
     493             :  *
     494             :  * @param osName Sub-group name.
     495             :  * @param papszOptions Driver specific options determining how the sub-group
     496             :  * should be opened.  Pass nullptr for default behavior.
     497             :  *
     498             :  * @return the group, or nullptr.
     499             :  */
     500             : std::shared_ptr<GDALGroup>
     501           4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
     502             :                      CPL_UNUSED CSLConstList papszOptions) const
     503             : {
     504           4 :     return nullptr;
     505             : }
     506             : 
     507             : /************************************************************************/
     508             : /*                        GetVectorLayerNames()                         */
     509             : /************************************************************************/
     510             : 
     511             : /** Return the list of layer names contained in this group.
     512             :  *
     513             :  * @note Driver implementation: optionally implemented. If implemented,
     514             :  * OpenVectorLayer() should also be implemented.
     515             :  *
     516             :  * Drivers known to implement it: OpenFileGDB, FileGDB
     517             :  *
     518             :  * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
     519             :  * GDALDataset::GetLayer() should then be used.
     520             :  *
     521             :  * This is the same as the C function GDALGroupGetVectorLayerNames().
     522             :  *
     523             :  * @param papszOptions Driver specific options determining how layers
     524             :  * should be retrieved. Pass nullptr for default behavior.
     525             :  *
     526             :  * @return the vector layer names.
     527             :  * @since GDAL 3.4
     528             :  */
     529             : std::vector<std::string>
     530           1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
     531             : {
     532           1 :     return {};
     533             : }
     534             : 
     535             : /************************************************************************/
     536             : /*                          OpenVectorLayer()                           */
     537             : /************************************************************************/
     538             : 
     539             : /** Open and return a vector layer.
     540             :  *
     541             :  * Due to the historical ownership of OGRLayer* by GDALDataset*, the
     542             :  * lifetime of the returned OGRLayer* is linked to the one of the owner
     543             :  * dataset (contrary to the general design of this class where objects can be
     544             :  * used independently of the object that returned them)
     545             :  *
     546             :  * @note Driver implementation: optionally implemented. If implemented,
     547             :  * GetVectorLayerNames() should also be implemented.
     548             :  *
     549             :  * Drivers known to implement it: MEM, netCDF.
     550             :  *
     551             :  * This is the same as the C function GDALGroupOpenVectorLayer().
     552             :  *
     553             :  * @param osName Vector layer name.
     554             :  * @param papszOptions Driver specific options determining how the layer should
     555             :  * be opened.  Pass nullptr for default behavior.
     556             :  *
     557             :  * @return the group, or nullptr.
     558             :  */
     559           2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
     560             :                                      CPL_UNUSED CSLConstList papszOptions) const
     561             : {
     562           2 :     return nullptr;
     563             : }
     564             : 
     565             : /************************************************************************/
     566             : /*                           GetDimensions()                            */
     567             : /************************************************************************/
     568             : 
     569             : /** Return the list of dimensions contained in this group and used by its
     570             :  * arrays.
     571             :  *
     572             :  * This is for dimensions that can potentially be used by several arrays.
     573             :  * Not all drivers might implement this. To retrieve the dimensions used by
     574             :  * a specific array, use GDALMDArray::GetDimensions().
     575             :  *
     576             :  * Drivers known to implement it: MEM, netCDF
     577             :  *
     578             :  * This is the same as the C function GDALGroupGetDimensions().
     579             :  *
     580             :  * @param papszOptions Driver specific options determining how groups
     581             :  * should be retrieved. Pass nullptr for default behavior.
     582             :  *
     583             :  * @return the dimensions.
     584             :  */
     585             : std::vector<std::shared_ptr<GDALDimension>>
     586          11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
     587             : {
     588          11 :     return {};
     589             : }
     590             : 
     591             : /************************************************************************/
     592             : /*                         GetStructuralInfo()                          */
     593             : /************************************************************************/
     594             : 
     595             : /** Return structural information on the group.
     596             :  *
     597             :  * This may be the compression, etc..
     598             :  *
     599             :  * The return value should not be freed and is valid until GDALGroup is
     600             :  * released or this function called again.
     601             :  *
     602             :  * This is the same as the C function GDALGroupGetStructuralInfo().
     603             :  */
     604          46 : CSLConstList GDALGroup::GetStructuralInfo() const
     605             : {
     606          46 :     return nullptr;
     607             : }
     608             : 
     609             : /************************************************************************/
     610             : /*                            CreateGroup()                             */
     611             : /************************************************************************/
     612             : 
     613             : /** Create a sub-group within a group.
     614             :  *
     615             :  * Optionally implemented by drivers.
     616             :  *
     617             :  * Drivers known to implement it: MEM, netCDF
     618             :  *
     619             :  * This is the same as the C function GDALGroupCreateGroup().
     620             :  *
     621             :  * @param osName Sub-group name.
     622             :  * @param papszOptions Driver specific options determining how the sub-group
     623             :  * should be created.
     624             :  *
     625             :  * @return the new sub-group, or nullptr in case of error.
     626             :  */
     627             : std::shared_ptr<GDALGroup>
     628           0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
     629             :                        CPL_UNUSED CSLConstList papszOptions)
     630             : {
     631           0 :     CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
     632           0 :     return nullptr;
     633             : }
     634             : 
     635             : /************************************************************************/
     636             : /*                            DeleteGroup()                             */
     637             : /************************************************************************/
     638             : 
     639             : /** Delete a sub-group from a group.
     640             :  *
     641             :  * Optionally implemented.
     642             :  *
     643             :  * After this call, if a previously obtained instance of the deleted object
     644             :  * is still alive, no method other than for freeing it should be invoked.
     645             :  *
     646             :  * Drivers known to implement it: MEM, Zarr
     647             :  *
     648             :  * This is the same as the C function GDALGroupDeleteGroup().
     649             :  *
     650             :  * @param osName Sub-group name.
     651             :  * @param papszOptions Driver specific options determining how the group.
     652             :  * should be deleted.
     653             :  *
     654             :  * @return true in case of success
     655             :  * @since GDAL 3.8
     656             :  */
     657           0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
     658             :                             CPL_UNUSED CSLConstList papszOptions)
     659             : {
     660           0 :     CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
     661           0 :     return false;
     662             : }
     663             : 
     664             : /************************************************************************/
     665             : /*                          CreateDimension()                           */
     666             : /************************************************************************/
     667             : 
     668             : /** Create a dimension within a group.
     669             :  *
     670             :  * @note Driver implementation: drivers supporting CreateDimension() should
     671             :  * implement this method, but do not have necessarily to implement
     672             :  * GDALGroup::GetDimensions().
     673             :  *
     674             :  * Drivers known to implement it: MEM, netCDF
     675             :  *
     676             :  * This is the same as the C function GDALGroupCreateDimension().
     677             :  *
     678             :  * @param osName Dimension name.
     679             :  * @param osType Dimension type (might be empty, and ignored by drivers)
     680             :  * @param osDirection Dimension direction (might be empty, and ignored by
     681             :  * drivers)
     682             :  * @param nSize  Number of values indexed by this dimension. Should be > 0.
     683             :  * @param papszOptions Driver specific options determining how the dimension
     684             :  * should be created.
     685             :  *
     686             :  * @return the new dimension, or nullptr if case of error
     687             :  */
     688           0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
     689             :     CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
     690             :     CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
     691             :     CPL_UNUSED CSLConstList papszOptions)
     692             : {
     693           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     694             :              "CreateDimension() not implemented");
     695           0 :     return nullptr;
     696             : }
     697             : 
     698             : /************************************************************************/
     699             : /*                           CreateMDArray()                            */
     700             : /************************************************************************/
     701             : 
     702             : /** Create a multidimensional array within a group.
     703             :  *
     704             :  * It is recommended that the GDALDimension objects passed in aoDimensions
     705             :  * belong to this group, either by retrieving them with GetDimensions()
     706             :  * or creating a new one with CreateDimension().
     707             :  *
     708             :  * Optionally implemented.
     709             :  *
     710             :  * Drivers known to implement it: MEM, netCDF
     711             :  *
     712             :  * This is the same as the C function GDALGroupCreateMDArray().
     713             :  *
     714             :  * @note Driver implementation: drivers should take into account the possibility
     715             :  * that GDALDimension object passed in aoDimensions might belong to a different
     716             :  * group / dataset / driver and act accordingly.
     717             :  *
     718             :  * @param osName Array name.
     719             :  * @param aoDimensions List of dimensions, ordered from the slowest varying
     720             :  *                     dimension first to the fastest varying dimension last.
     721             :  *                     Might be empty for a scalar array (if supported by
     722             :  * driver)
     723             :  * @param oDataType  Array data type.
     724             :  * @param papszOptions Driver specific options determining how the array
     725             :  * should be created.
     726             :  *
     727             :  * @return the new array, or nullptr in case of error
     728             :  */
     729           0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
     730             :     CPL_UNUSED const std::string &osName,
     731             :     CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
     732             :     CPL_UNUSED const GDALExtendedDataType &oDataType,
     733             :     CPL_UNUSED CSLConstList papszOptions)
     734             : {
     735           0 :     CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
     736           0 :     return nullptr;
     737             : }
     738             : 
     739             : /************************************************************************/
     740             : /*                           DeleteMDArray()                            */
     741             : /************************************************************************/
     742             : 
     743             : /** Delete an array from a group.
     744             :  *
     745             :  * Optionally implemented.
     746             :  *
     747             :  * After this call, if a previously obtained instance of the deleted object
     748             :  * is still alive, no method other than for freeing it should be invoked.
     749             :  *
     750             :  * Drivers known to implement it: MEM, Zarr
     751             :  *
     752             :  * This is the same as the C function GDALGroupDeleteMDArray().
     753             :  *
     754             :  * @param osName Arrayname.
     755             :  * @param papszOptions Driver specific options determining how the array.
     756             :  * should be deleted.
     757             :  *
     758             :  * @return true in case of success
     759             :  * @since GDAL 3.8
     760             :  */
     761           0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
     762             :                               CPL_UNUSED CSLConstList papszOptions)
     763             : {
     764           0 :     CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
     765           0 :     return false;
     766             : }
     767             : 
     768             : /************************************************************************/
     769             : /*                          GetTotalCopyCost()                          */
     770             : /************************************************************************/
     771             : 
     772             : /** Return a total "cost" to copy the group.
     773             :  *
     774             :  * Used as a parameter for CopFrom()
     775             :  */
     776          33 : GUInt64 GDALGroup::GetTotalCopyCost() const
     777             : {
     778          33 :     GUInt64 nCost = COPY_COST;
     779          33 :     nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
     780             : 
     781          66 :     auto groupNames = GetGroupNames();
     782          39 :     for (const auto &name : groupNames)
     783             :     {
     784          12 :         auto subGroup = OpenGroup(name);
     785           6 :         if (subGroup)
     786             :         {
     787           6 :             nCost += subGroup->GetTotalCopyCost();
     788             :         }
     789             :     }
     790             : 
     791          33 :     auto arrayNames = GetMDArrayNames();
     792         102 :     for (const auto &name : arrayNames)
     793             :     {
     794         138 :         auto array = OpenMDArray(name);
     795          69 :         if (array)
     796             :         {
     797          69 :             nCost += array->GetTotalCopyCost();
     798             :         }
     799             :     }
     800          66 :     return nCost;
     801             : }
     802             : 
     803             : /************************************************************************/
     804             : /*                              CopyFrom()                              */
     805             : /************************************************************************/
     806             : 
     807             : /** Copy the content of a group into a new (generally empty) group.
     808             :  *
     809             :  * @param poDstRootGroup Destination root group. Must NOT be nullptr.
     810             :  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
     811             :  *                   of some output drivers this is not recommended)
     812             :  * @param poSrcGroup Source group. Must NOT be nullptr.
     813             :  * @param bStrict Whether to enable strict mode. In strict mode, any error will
     814             :  *                stop the copy. In relaxed mode, the copy will be attempted to
     815             :  *                be pursued.
     816             :  * @param nCurCost  Should be provided as a variable initially set to 0.
     817             :  * @param nTotalCost Total cost from GetTotalCopyCost().
     818             :  * @param pfnProgress Progress callback, or nullptr.
     819             :  * @param pProgressData Progress user data, or nullptr.
     820             :  * @param papszOptions Creation options. Currently, only array creation
     821             :  *                     options are supported. They must be prefixed with
     822             :  * "ARRAY:" . The scope may be further restricted to arrays of a certain
     823             :  *                     dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
     824             :  *                     For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
     825             :  *                     restrict BLOCKSIZE=256,256 to arrays of dimension 2.
     826             :  *                     Restriction to arrays of a given name is done with adding
     827             :  *                     "IF(NAME={name}):" after "ARRAY:". {name} can also be
     828             :  *                     a full qualified name.
     829             :  *                     A non-driver specific ARRAY option, "AUTOSCALE=YES" can
     830             :  * be used to ask (non indexing) variables of type Float32 or Float64 to be
     831             :  * scaled to UInt16 with scale and offset values being computed from the minimum
     832             :  * and maximum of the source array. The integer data type used can be set with
     833             :  *                     AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
     834             :  *
     835             :  * @return true in case of success (or partial success if bStrict == false).
     836             :  */
     837          33 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
     838             :                          GDALDataset *poSrcDS,
     839             :                          const std::shared_ptr<GDALGroup> &poSrcGroup,
     840             :                          bool bStrict, GUInt64 &nCurCost,
     841             :                          const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
     842             :                          void *pProgressData, CSLConstList papszOptions)
     843             : {
     844          33 :     if (pfnProgress == nullptr)
     845           0 :         pfnProgress = GDALDummyProgress;
     846             : 
     847             : #define EXIT_OR_CONTINUE_IF_NULL(x)                                            \
     848             :     if (!(x))                                                                  \
     849             :     {                                                                          \
     850             :         if (bStrict)                                                           \
     851             :             return false;                                                      \
     852             :         continue;                                                              \
     853             :     }                                                                          \
     854             :     (void)0
     855             : 
     856             :     try
     857             :     {
     858          33 :         nCurCost += GDALGroup::COPY_COST;
     859             : 
     860          66 :         const auto srcDims = poSrcGroup->GetDimensions();
     861             :         std::map<std::string, std::shared_ptr<GDALDimension>>
     862          66 :             mapExistingDstDims;
     863          66 :         std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
     864          87 :         for (const auto &dim : srcDims)
     865             :         {
     866             :             auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
     867          54 :                                           dim->GetDirection(), dim->GetSize());
     868          54 :             EXIT_OR_CONTINUE_IF_NULL(dstDim);
     869          54 :             mapExistingDstDims[dim->GetName()] = std::move(dstDim);
     870         108 :             auto poIndexingVarSrc(dim->GetIndexingVariable());
     871          54 :             if (poIndexingVarSrc)
     872             :             {
     873             :                 mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
     874          35 :                                                        ->GetName()] =
     875          70 :                     dim->GetName();
     876             :             }
     877             :         }
     878             : 
     879          66 :         auto attrs = poSrcGroup->GetAttributes();
     880          51 :         for (const auto &attr : attrs)
     881             :         {
     882             :             auto dstAttr =
     883          18 :                 CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
     884          36 :                                 attr->GetDataType());
     885          18 :             EXIT_OR_CONTINUE_IF_NULL(dstAttr);
     886          18 :             auto raw(attr->ReadAsRaw());
     887          18 :             if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
     888           0 :                 return false;
     889             :         }
     890          33 :         if (!attrs.empty())
     891             :         {
     892           8 :             nCurCost += attrs.size() * GDALAttribute::COPY_COST;
     893           8 :             if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
     894           0 :                 return false;
     895             :         }
     896             : 
     897             :         const auto CopyArray =
     898          69 :             [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
     899             :              &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
     900             :              papszOptions, bStrict, &nCurCost,
     901         616 :              nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
     902             :         {
     903             :             // Map source dimensions to target dimensions
     904         138 :             std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
     905          69 :             const auto &srcArrayDims(srcArray->GetDimensions());
     906         172 :             for (const auto &dim : srcArrayDims)
     907             :             {
     908             :                 auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
     909         103 :                     dim->GetFullName());
     910         103 :                 if (dstDim && dstDim->GetSize() == dim->GetSize())
     911             :                 {
     912          93 :                     dstArrayDims.emplace_back(dstDim);
     913             :                 }
     914             :                 else
     915             :                 {
     916          10 :                     auto oIter = mapExistingDstDims.find(dim->GetName());
     917          19 :                     if (oIter != mapExistingDstDims.end() &&
     918           9 :                         oIter->second->GetSize() == dim->GetSize())
     919             :                     {
     920           8 :                         dstArrayDims.emplace_back(oIter->second);
     921             :                     }
     922             :                     else
     923             :                     {
     924           2 :                         std::string newDimName;
     925           2 :                         if (oIter == mapExistingDstDims.end())
     926             :                         {
     927           1 :                             newDimName = dim->GetName();
     928             :                         }
     929             :                         else
     930             :                         {
     931           1 :                             std::string newDimNamePrefix(srcArray->GetName() +
     932           3 :                                                          '_' + dim->GetName());
     933           1 :                             newDimName = newDimNamePrefix;
     934           1 :                             int nIterCount = 2;
     935           0 :                             while (
     936           1 :                                 cpl::contains(mapExistingDstDims, newDimName))
     937             :                             {
     938           0 :                                 newDimName = newDimNamePrefix +
     939           0 :                                              CPLSPrintf("_%d", nIterCount);
     940           0 :                                 nIterCount++;
     941             :                             }
     942             :                         }
     943           4 :                         dstDim = CreateDimension(newDimName, dim->GetType(),
     944             :                                                  dim->GetDirection(),
     945           4 :                                                  dim->GetSize());
     946           2 :                         if (!dstDim)
     947           0 :                             return false;
     948           2 :                         mapExistingDstDims[newDimName] = dstDim;
     949           2 :                         dstArrayDims.emplace_back(dstDim);
     950             :                     }
     951             :                 }
     952             :             }
     953             : 
     954         138 :             CPLStringList aosArrayCO;
     955          69 :             bool bAutoScale = false;
     956          69 :             GDALDataType eAutoScaleType = GDT_UInt16;
     957          76 :             for (const char *pszItem : cpl::Iterate(papszOptions))
     958             :             {
     959           7 :                 if (STARTS_WITH_CI(pszItem, "ARRAY:"))
     960             :                 {
     961           7 :                     const char *pszOption = pszItem + strlen("ARRAY:");
     962           7 :                     if (STARTS_WITH_CI(pszOption, "IF(DIM="))
     963             :                     {
     964           1 :                         const char *pszNext = strchr(pszOption, ':');
     965           1 :                         if (pszNext != nullptr)
     966             :                         {
     967           1 :                             int nDim = atoi(pszOption + strlen("IF(DIM="));
     968           1 :                             if (static_cast<size_t>(nDim) ==
     969           1 :                                 dstArrayDims.size())
     970             :                             {
     971           1 :                                 pszOption = pszNext + 1;
     972             :                             }
     973             :                             else
     974             :                             {
     975           0 :                                 pszOption = nullptr;
     976             :                             }
     977             :                         }
     978             :                     }
     979           6 :                     else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
     980             :                     {
     981           2 :                         const char *pszName = pszOption + strlen("IF(NAME=");
     982           2 :                         const char *pszNext = strchr(pszName, ':');
     983           2 :                         if (pszNext != nullptr && pszNext > pszName &&
     984           2 :                             pszNext[-1] == ')')
     985             :                         {
     986           4 :                             CPLString osName;
     987           2 :                             osName.assign(pszName, pszNext - pszName - 1);
     988           3 :                             if (osName == srcArray->GetName() ||
     989           1 :                                 osName == srcArray->GetFullName())
     990             :                             {
     991           2 :                                 pszOption = pszNext + 1;
     992             :                             }
     993             :                             else
     994             :                             {
     995           0 :                                 pszOption = nullptr;
     996             :                             }
     997             :                         }
     998             :                     }
     999           7 :                     if (pszOption)
    1000             :                     {
    1001           7 :                         if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
    1002             :                         {
    1003             :                             bAutoScale =
    1004           2 :                                 CPLTestBool(pszOption + strlen("AUTOSCALE="));
    1005             :                         }
    1006           5 :                         else if (STARTS_WITH_CI(pszOption,
    1007             :                                                 "AUTOSCALE_DATA_TYPE="))
    1008             :                         {
    1009           1 :                             const char *pszDataType =
    1010             :                                 pszOption + strlen("AUTOSCALE_DATA_TYPE=");
    1011           1 :                             eAutoScaleType = GDALGetDataTypeByName(pszDataType);
    1012           2 :                             if (GDALDataTypeIsComplex(eAutoScaleType) ||
    1013           1 :                                 GDALDataTypeIsFloating(eAutoScaleType))
    1014             :                             {
    1015           0 :                                 CPLError(CE_Failure, CPLE_NotSupported,
    1016             :                                          "Unsupported value for "
    1017             :                                          "AUTOSCALE_DATA_TYPE");
    1018           0 :                                 return false;
    1019             :                             }
    1020             :                         }
    1021             :                         else
    1022             :                         {
    1023           4 :                             aosArrayCO.AddString(pszOption);
    1024             :                         }
    1025             :                     }
    1026             :                 }
    1027             :             }
    1028             : 
    1029          69 :             if (aosArrayCO.FetchNameValue("BLOCKSIZE") == nullptr)
    1030             :             {
    1031         136 :                 const auto anBlockSize = srcArray->GetBlockSize();
    1032         136 :                 std::string osBlockSize;
    1033          74 :                 for (auto v : anBlockSize)
    1034             :                 {
    1035          69 :                     if (v == 0)
    1036             :                     {
    1037          63 :                         osBlockSize.clear();
    1038          63 :                         break;
    1039             :                     }
    1040           6 :                     if (!osBlockSize.empty())
    1041           2 :                         osBlockSize += ',';
    1042           6 :                     osBlockSize += std::to_string(v);
    1043             :                 }
    1044          68 :                 if (!osBlockSize.empty())
    1045           3 :                     aosArrayCO.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
    1046             :             }
    1047             : 
    1048             :             auto oIterDimName =
    1049          69 :                 mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
    1050          69 :             const auto &srcArrayType = srcArray->GetDataType();
    1051             : 
    1052          69 :             std::shared_ptr<GDALMDArray> dstArray;
    1053             : 
    1054             :             // Only autoscale non-indexing variables
    1055          69 :             bool bHasOffset = false;
    1056          69 :             bool bHasScale = false;
    1057           4 :             if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
    1058           4 :                 (srcArrayType.GetNumericDataType() == GDT_Float16 ||
    1059           2 :                  srcArrayType.GetNumericDataType() == GDT_Float32 ||
    1060           0 :                  srcArrayType.GetNumericDataType() == GDT_Float64) &&
    1061           2 :                 srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
    1062          73 :                 srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
    1063          71 :                 oIterDimName == mapSrcVariableNameToIndexedDimName.end())
    1064             :             {
    1065           2 :                 constexpr bool bApproxOK = false;
    1066           2 :                 constexpr bool bForce = true;
    1067           2 :                 double dfMin = 0.0;
    1068           2 :                 double dfMax = 0.0;
    1069           2 :                 if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
    1070             :                                             nullptr, nullptr, nullptr, nullptr,
    1071           2 :                                             nullptr) != CE_None)
    1072             :                 {
    1073           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1074             :                              "Could not retrieve statistics for array %s",
    1075           0 :                              srcArray->GetName().c_str());
    1076           0 :                     return false;
    1077             :                 }
    1078           2 :                 double dfDTMin = 0;
    1079           2 :                 double dfDTMax = 0;
    1080             : #define setDTMinMax(ctype)                                                     \
    1081             :     do                                                                         \
    1082             :     {                                                                          \
    1083             :         dfDTMin = static_cast<double>(cpl::NumericLimits<ctype>::lowest());    \
    1084             :         dfDTMax = static_cast<double>(cpl::NumericLimits<ctype>::max());       \
    1085             :     } while (0)
    1086             : 
    1087           2 :                 switch (eAutoScaleType)
    1088             :                 {
    1089           0 :                     case GDT_UInt8:
    1090           0 :                         setDTMinMax(GByte);
    1091           0 :                         break;
    1092           0 :                     case GDT_Int8:
    1093           0 :                         setDTMinMax(GInt8);
    1094           0 :                         break;
    1095           1 :                     case GDT_UInt16:
    1096           1 :                         setDTMinMax(GUInt16);
    1097           1 :                         break;
    1098           1 :                     case GDT_Int16:
    1099           1 :                         setDTMinMax(GInt16);
    1100           1 :                         break;
    1101           0 :                     case GDT_UInt32:
    1102           0 :                         setDTMinMax(GUInt32);
    1103           0 :                         break;
    1104           0 :                     case GDT_Int32:
    1105           0 :                         setDTMinMax(GInt32);
    1106           0 :                         break;
    1107           0 :                     case GDT_UInt64:
    1108           0 :                         setDTMinMax(std::uint64_t);
    1109           0 :                         break;
    1110           0 :                     case GDT_Int64:
    1111           0 :                         setDTMinMax(std::int64_t);
    1112           0 :                         break;
    1113           0 :                     case GDT_Float16:
    1114             :                     case GDT_Float32:
    1115             :                     case GDT_Float64:
    1116             :                     case GDT_Unknown:
    1117             :                     case GDT_CInt16:
    1118             :                     case GDT_CInt32:
    1119             :                     case GDT_CFloat16:
    1120             :                     case GDT_CFloat32:
    1121             :                     case GDT_CFloat64:
    1122             :                     case GDT_TypeCount:
    1123           0 :                         CPLAssert(false);
    1124             :                 }
    1125             : 
    1126             :                 dstArray =
    1127           4 :                     CreateMDArray(srcArray->GetName(), dstArrayDims,
    1128           4 :                                   GDALExtendedDataType::Create(eAutoScaleType),
    1129           4 :                                   aosArrayCO.List());
    1130           2 :                 if (!dstArray)
    1131           0 :                     return !bStrict;
    1132             : 
    1133           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1134             :                 {
    1135             :                     // If there's a nodata value in the source array, reserve
    1136             :                     // DTMax for that purpose in the target scaled array
    1137           1 :                     if (!dstArray->SetNoDataValue(dfDTMax))
    1138             :                     {
    1139           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    1140             :                                  "Cannot set nodata value");
    1141           0 :                         return false;
    1142             :                     }
    1143           1 :                     dfDTMax--;
    1144             :                 }
    1145           2 :                 const double dfScale =
    1146           2 :                     dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
    1147           2 :                 const double dfOffset = dfMin - dfDTMin * dfScale;
    1148             : 
    1149           4 :                 if (!dstArray->SetOffset(dfOffset) ||
    1150           2 :                     !dstArray->SetScale(dfScale))
    1151             :                 {
    1152           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1153             :                              "Cannot set scale/offset");
    1154           0 :                     return false;
    1155             :                 }
    1156             : 
    1157           2 :                 auto poUnscaled = dstArray->GetUnscaled();
    1158           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1159             :                 {
    1160           1 :                     poUnscaled->SetNoDataValue(
    1161             :                         srcArray->GetNoDataValueAsDouble());
    1162             :                 }
    1163             : 
    1164             :                 // Copy source array into unscaled array
    1165           4 :                 if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1166             :                                           nCurCost, nTotalCost, pfnProgress,
    1167           2 :                                           pProgressData))
    1168             :                 {
    1169           0 :                     return false;
    1170             :                 }
    1171             :             }
    1172             :             else
    1173             :             {
    1174         134 :                 dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
    1175         134 :                                          srcArrayType, aosArrayCO.List());
    1176          67 :                 if (!dstArray)
    1177           0 :                     return !bStrict;
    1178             : 
    1179         134 :                 if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1180             :                                         nCurCost, nTotalCost, pfnProgress,
    1181          67 :                                         pProgressData))
    1182             :                 {
    1183           0 :                     return false;
    1184             :                 }
    1185             :             }
    1186             : 
    1187             :             // If this array is the indexing variable of a dimension, link them
    1188             :             // together.
    1189          69 :             if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
    1190             :             {
    1191             :                 auto oCorrespondingDimIter =
    1192          35 :                     mapExistingDstDims.find(oIterDimName->second);
    1193          35 :                 if (oCorrespondingDimIter != mapExistingDstDims.end())
    1194             :                 {
    1195             :                     CPLErrorStateBackuper oErrorStateBackuper(
    1196          35 :                         CPLQuietErrorHandler);
    1197          70 :                     oCorrespondingDimIter->second->SetIndexingVariable(
    1198          35 :                         std::move(dstArray));
    1199             :                 }
    1200             :             }
    1201             : 
    1202          69 :             return true;
    1203          33 :         };
    1204             : 
    1205          66 :         const auto arrayNames = poSrcGroup->GetMDArrayNames();
    1206             : 
    1207             :         // Start by copying arrays that are indexing variables of dimensions
    1208         102 :         for (const auto &name : arrayNames)
    1209             :         {
    1210          69 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1211          69 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1212             : 
    1213          69 :             if (cpl::contains(mapSrcVariableNameToIndexedDimName,
    1214          69 :                               srcArray->GetName()))
    1215             :             {
    1216          35 :                 if (!CopyArray(srcArray))
    1217           0 :                     return false;
    1218             :             }
    1219             :         }
    1220             : 
    1221             :         // Then copy regular arrays
    1222         102 :         for (const auto &name : arrayNames)
    1223             :         {
    1224          69 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1225          69 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1226             : 
    1227          69 :             if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
    1228          69 :                                srcArray->GetName()))
    1229             :             {
    1230          34 :                 if (!CopyArray(srcArray))
    1231           0 :                     return false;
    1232             :             }
    1233             :         }
    1234             : 
    1235          66 :         const auto groupNames = poSrcGroup->GetGroupNames();
    1236          39 :         for (const auto &name : groupNames)
    1237             :         {
    1238           6 :             auto srcSubGroup = poSrcGroup->OpenGroup(name);
    1239           6 :             EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
    1240           6 :             auto dstSubGroup = CreateGroup(name);
    1241           6 :             EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
    1242          12 :             if (!dstSubGroup->CopyFrom(
    1243             :                     poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
    1244           6 :                     nTotalCost, pfnProgress, pProgressData, papszOptions))
    1245           0 :                 return false;
    1246             :         }
    1247             : 
    1248          33 :         if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    1249           0 :             return false;
    1250             : 
    1251          33 :         return true;
    1252             :     }
    1253           0 :     catch (const std::exception &e)
    1254             :     {
    1255           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1256           0 :         return false;
    1257             :     }
    1258             : }
    1259             : 
    1260             : /************************************************************************/
    1261             : /*                         GetInnerMostGroup()                          */
    1262             : /************************************************************************/
    1263             : 
    1264             : //! @cond Doxygen_Suppress
    1265             : const GDALGroup *
    1266        2285 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
    1267             :                              std::shared_ptr<GDALGroup> &curGroupHolder,
    1268             :                              std::string &osLastPart) const
    1269             : {
    1270        2285 :     if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
    1271           6 :         return nullptr;
    1272        2279 :     const GDALGroup *poCurGroup = this;
    1273             :     CPLStringList aosTokens(
    1274        4558 :         CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
    1275        2279 :     if (aosTokens.size() == 0)
    1276             :     {
    1277             :         // "/" case: the root group itself is the innermost group
    1278           2 :         osLastPart.clear();
    1279           2 :         return poCurGroup;
    1280             :     }
    1281             : 
    1282        2798 :     for (int i = 0; i < aosTokens.size() - 1; i++)
    1283             :     {
    1284         533 :         curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
    1285         533 :         if (!curGroupHolder)
    1286             :         {
    1287          12 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
    1288             :                      aosTokens[i]);
    1289          12 :             return nullptr;
    1290             :         }
    1291         521 :         poCurGroup = curGroupHolder.get();
    1292             :     }
    1293        2265 :     osLastPart = aosTokens[aosTokens.size() - 1];
    1294        2265 :     return poCurGroup;
    1295             : }
    1296             : 
    1297             : //! @endcond
    1298             : 
    1299             : /************************************************************************/
    1300             : /*                      OpenMDArrayFromFullname()                       */
    1301             : /************************************************************************/
    1302             : 
    1303             : /** Get an array from its fully qualified name */
    1304             : std::shared_ptr<GDALMDArray>
    1305         806 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
    1306             :                                    CSLConstList papszOptions) const
    1307             : {
    1308        1612 :     std::string osName;
    1309         806 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1310         806 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1311         806 :     if (poGroup == nullptr)
    1312          12 :         return nullptr;
    1313         794 :     return poGroup->OpenMDArray(osName, papszOptions);
    1314             : }
    1315             : 
    1316             : /************************************************************************/
    1317             : /*                     OpenAttributeFromFullname()                      */
    1318             : /************************************************************************/
    1319             : 
    1320             : /** Get an attribute from its fully qualified name */
    1321             : std::shared_ptr<GDALAttribute>
    1322           9 : GDALGroup::OpenAttributeFromFullname(const std::string &osFullName,
    1323             :                                      CSLConstList papszOptions) const
    1324             : {
    1325           9 :     const auto pos = osFullName.rfind('/');
    1326           9 :     if (pos == std::string::npos)
    1327           0 :         return nullptr;
    1328          18 :     const std::string attrName = osFullName.substr(pos + 1);
    1329           9 :     if (pos == 0)
    1330           2 :         return GetAttribute(attrName);
    1331          14 :     const std::string container = osFullName.substr(0, pos);
    1332          14 :     auto poArray = OpenMDArrayFromFullname(container, papszOptions);
    1333           7 :     if (poArray)
    1334           4 :         return poArray->GetAttribute(attrName);
    1335           6 :     auto poGroup = OpenGroupFromFullname(container, papszOptions);
    1336           3 :     if (poGroup)
    1337           1 :         return poGroup->GetAttribute(attrName);
    1338           2 :     return nullptr;
    1339             : }
    1340             : 
    1341             : /************************************************************************/
    1342             : /*                           ResolveMDArray()                           */
    1343             : /************************************************************************/
    1344             : 
    1345             : /** Locate an array in a group and its subgroups by name.
    1346             :  *
    1347             :  * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
    1348             :  * used
    1349             :  * Otherwise the search will start from the group identified by osStartingPath,
    1350             :  * and an array whose name is osName will be looked for in this group (if
    1351             :  * osStartingPath is empty or "/", then the current group is used). If there
    1352             :  * is no match, then a recursive descendant search will be made in its
    1353             :  * subgroups. If there is no match in the subgroups, then the parent (if
    1354             :  * existing) of the group pointed by osStartingPath will be used as the new
    1355             :  * starting point for the search.
    1356             :  *
    1357             :  * @param osName name, qualified or not
    1358             :  * @param osStartingPath fully qualified name of the (sub-)group from which
    1359             :  *                       the search should be started. If this is a non-empty
    1360             :  *                       string, the group on which this method is called should
    1361             :  *                       nominally be the root group (otherwise the path will
    1362             :  *                       be interpreted as from the current group)
    1363             :  * @param papszOptions options to pass to OpenMDArray()
    1364             :  * @since GDAL 3.2
    1365             :  */
    1366             : std::shared_ptr<GDALMDArray>
    1367          19 : GDALGroup::ResolveMDArray(const std::string &osName,
    1368             :                           const std::string &osStartingPath,
    1369             :                           CSLConstList papszOptions) const
    1370             : {
    1371          19 :     if (!osName.empty() && osName[0] == '/')
    1372             :     {
    1373           1 :         auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
    1374           1 :         if (poArray)
    1375           1 :             return poArray;
    1376             :     }
    1377          36 :     std::string osPath(osStartingPath);
    1378          36 :     std::set<std::string> oSetAlreadyVisited;
    1379             : 
    1380             :     while (true)
    1381             :     {
    1382           0 :         std::shared_ptr<GDALGroup> curGroupHolder;
    1383           0 :         std::shared_ptr<GDALGroup> poGroup;
    1384             : 
    1385          22 :         std::queue<std::shared_ptr<GDALGroup>> oQueue;
    1386          22 :         bool goOn = false;
    1387          22 :         if (osPath.empty() || osPath == "/")
    1388             :         {
    1389          11 :             goOn = true;
    1390             :         }
    1391             :         else
    1392             :         {
    1393          22 :             std::string osLastPart;
    1394             :             const GDALGroup *poGroupPtr =
    1395          11 :                 GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
    1396          11 :             if (poGroupPtr)
    1397          11 :                 poGroup = poGroupPtr->OpenGroup(osLastPart);
    1398          22 :             if (poGroup &&
    1399          22 :                 !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
    1400             :             {
    1401          11 :                 oQueue.push(poGroup);
    1402          11 :                 goOn = true;
    1403             :             }
    1404             :         }
    1405             : 
    1406          22 :         if (goOn)
    1407             :         {
    1408          17 :             do
    1409             :             {
    1410             :                 const GDALGroup *groupPtr;
    1411          39 :                 if (!oQueue.empty())
    1412             :                 {
    1413          28 :                     poGroup = oQueue.front();
    1414          28 :                     oQueue.pop();
    1415          28 :                     groupPtr = poGroup.get();
    1416             :                 }
    1417             :                 else
    1418             :                 {
    1419          11 :                     groupPtr = this;
    1420             :                 }
    1421             : 
    1422          39 :                 auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
    1423          39 :                 if (poArray)
    1424          16 :                     return poArray;
    1425             : 
    1426          46 :                 const auto aosGroupNames = groupPtr->GetGroupNames();
    1427          47 :                 for (const auto &osGroupName : aosGroupNames)
    1428             :                 {
    1429          48 :                     auto poSubGroup = groupPtr->OpenGroup(osGroupName);
    1430          48 :                     if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
    1431          48 :                                                      poSubGroup->GetFullName()))
    1432             :                     {
    1433          24 :                         oQueue.push(poSubGroup);
    1434          24 :                         oSetAlreadyVisited.insert(poSubGroup->GetFullName());
    1435             :                     }
    1436             :                 }
    1437          23 :             } while (!oQueue.empty());
    1438             :         }
    1439             : 
    1440           6 :         if (osPath.empty() || osPath == "/")
    1441           2 :             break;
    1442             : 
    1443           4 :         const auto nPos = osPath.rfind('/');
    1444           4 :         if (nPos == 0)
    1445           1 :             osPath = "/";
    1446             :         else
    1447             :         {
    1448           3 :             if (nPos == std::string::npos)
    1449           0 :                 break;
    1450           3 :             osPath.resize(nPos);
    1451             :         }
    1452           4 :     }
    1453           2 :     return nullptr;
    1454             : }
    1455             : 
    1456             : /************************************************************************/
    1457             : /*                       OpenGroupFromFullname()                        */
    1458             : /************************************************************************/
    1459             : 
    1460             : /** Get a group from its fully qualified name.
    1461             :  * @since GDAL 3.2
    1462             :  */
    1463             : std::shared_ptr<GDALGroup>
    1464        1263 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
    1465             :                                  CSLConstList papszOptions) const
    1466             : {
    1467        2526 :     std::string osName;
    1468        1263 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1469        1263 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1470        1263 :     if (poGroup == nullptr)
    1471           4 :         return nullptr;
    1472        1259 :     if (osName.empty())
    1473           2 :         return m_pSelf.lock();
    1474        1257 :     return poGroup->OpenGroup(osName, papszOptions);
    1475             : }
    1476             : 
    1477             : /************************************************************************/
    1478             : /*                     OpenDimensionFromFullname()                      */
    1479             : /************************************************************************/
    1480             : 
    1481             : /** Get a dimension from its fully qualified name */
    1482             : std::shared_ptr<GDALDimension>
    1483         205 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
    1484             : {
    1485         410 :     std::string osName;
    1486         205 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1487         205 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1488         205 :     if (poGroup == nullptr)
    1489           2 :         return nullptr;
    1490         406 :     auto dims(poGroup->GetDimensions());
    1491         348 :     for (auto &dim : dims)
    1492             :     {
    1493         296 :         if (dim->GetName() == osName)
    1494         151 :             return dim;
    1495             :     }
    1496          52 :     return nullptr;
    1497             : }
    1498             : 
    1499             : /************************************************************************/
    1500             : /*                          ClearStatistics()                           */
    1501             : /************************************************************************/
    1502             : 
    1503             : /**
    1504             :  * \brief Clear statistics.
    1505             :  *
    1506             :  * @since GDAL 3.4
    1507             :  */
    1508           0 : void GDALGroup::ClearStatistics()
    1509             : {
    1510           0 :     auto groupNames = GetGroupNames();
    1511           0 :     for (const auto &name : groupNames)
    1512             :     {
    1513           0 :         auto subGroup = OpenGroup(name);
    1514           0 :         if (subGroup)
    1515             :         {
    1516           0 :             subGroup->ClearStatistics();
    1517             :         }
    1518             :     }
    1519             : 
    1520           0 :     auto arrayNames = GetMDArrayNames();
    1521           0 :     for (const auto &name : arrayNames)
    1522             :     {
    1523           0 :         auto array = OpenMDArray(name);
    1524           0 :         if (array)
    1525             :         {
    1526           0 :             array->ClearStatistics();
    1527             :         }
    1528             :     }
    1529           0 : }
    1530             : 
    1531             : /************************************************************************/
    1532             : /*                               Rename()                               */
    1533             : /************************************************************************/
    1534             : 
    1535             : /** Rename the group.
    1536             :  *
    1537             :  * This is not implemented by all drivers.
    1538             :  *
    1539             :  * Drivers known to implement it: MEM, netCDF, ZARR.
    1540             :  *
    1541             :  * This is the same as the C function GDALGroupRename().
    1542             :  *
    1543             :  * @param osNewName New name.
    1544             :  *
    1545             :  * @return true in case of success
    1546             :  * @since GDAL 3.8
    1547             :  */
    1548           0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
    1549             : {
    1550           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
    1551           0 :     return false;
    1552             : }
    1553             : 
    1554             : /************************************************************************/
    1555             : /*                             BaseRename()                             */
    1556             : /************************************************************************/
    1557             : 
    1558             : //! @cond Doxygen_Suppress
    1559           8 : void GDALGroup::BaseRename(const std::string &osNewName)
    1560             : {
    1561           8 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
    1562           8 :     m_osFullName += osNewName;
    1563           8 :     m_osName = osNewName;
    1564             : 
    1565           8 :     NotifyChildrenOfRenaming();
    1566           8 : }
    1567             : 
    1568             : //! @endcond
    1569             : 
    1570             : /************************************************************************/
    1571             : /*                           ParentRenamed()                            */
    1572             : /************************************************************************/
    1573             : 
    1574             : //! @cond Doxygen_Suppress
    1575           7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
    1576             : {
    1577           7 :     m_osFullName = osNewParentFullName;
    1578           7 :     m_osFullName += "/";
    1579           7 :     m_osFullName += m_osName;
    1580             : 
    1581           7 :     NotifyChildrenOfRenaming();
    1582           7 : }
    1583             : 
    1584             : //! @endcond
    1585             : 
    1586             : /************************************************************************/
    1587             : /*                              Deleted()                               */
    1588             : /************************************************************************/
    1589             : 
    1590             : //! @cond Doxygen_Suppress
    1591          28 : void GDALGroup::Deleted()
    1592             : {
    1593          28 :     m_bValid = false;
    1594             : 
    1595          28 :     NotifyChildrenOfDeletion();
    1596          28 : }
    1597             : 
    1598             : //! @endcond
    1599             : 
    1600             : /************************************************************************/
    1601             : /*                           ParentDeleted()                            */
    1602             : /************************************************************************/
    1603             : 
    1604             : //! @cond Doxygen_Suppress
    1605           3 : void GDALGroup::ParentDeleted()
    1606             : {
    1607           3 :     Deleted();
    1608           3 : }
    1609             : 
    1610             : //! @endcond
    1611             : 
    1612             : /************************************************************************/
    1613             : /*                     CheckValidAndErrorOutIfNot()                     */
    1614             : /************************************************************************/
    1615             : 
    1616             : //! @cond Doxygen_Suppress
    1617       40261 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
    1618             : {
    1619       40261 :     if (!m_bValid)
    1620             :     {
    1621          14 :         CPLError(CE_Failure, CPLE_AppDefined,
    1622             :                  "This object has been deleted. No action on it is possible");
    1623             :     }
    1624       40261 :     return m_bValid;
    1625             : }
    1626             : 
    1627             : //! @endcond
    1628             : 
    1629             : /************************************************************************/
    1630             : /*                        ~GDALAbstractMDArray()                        */
    1631             : /************************************************************************/
    1632             : 
    1633             : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
    1634             : 
    1635             : /************************************************************************/
    1636             : /*                        GDALAbstractMDArray()                         */
    1637             : /************************************************************************/
    1638             : 
    1639             : //! @cond Doxygen_Suppress
    1640      171867 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
    1641      171867 :                                          const std::string &osName)
    1642             :     : m_osName(osName),
    1643             :       m_osFullName(
    1644      171867 :           !osParentName.empty()
    1645      340761 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
    1646      684495 :               : osName)
    1647             : {
    1648      171867 : }
    1649             : 
    1650             : //! @endcond
    1651             : 
    1652             : /************************************************************************/
    1653             : /*                           GetDimensions()                            */
    1654             : /************************************************************************/
    1655             : 
    1656             : /** \fn GDALAbstractMDArray::GetDimensions() const
    1657             :  * \brief Return the dimensions of an attribute/array.
    1658             :  *
    1659             :  * This is the same as the C functions GDALMDArrayGetDimensions() and
    1660             :  * similar to GDALAttributeGetDimensionsSize().
    1661             :  */
    1662             : 
    1663             : /************************************************************************/
    1664             : /*                            GetDataType()                             */
    1665             : /************************************************************************/
    1666             : 
    1667             : /** \fn GDALAbstractMDArray::GetDataType() const
    1668             :  * \brief Return the data type of an attribute/array.
    1669             :  *
    1670             :  * This is the same as the C functions GDALMDArrayGetDataType() and
    1671             :  * GDALAttributeGetDataType()
    1672             :  */
    1673             : 
    1674             : /************************************************************************/
    1675             : /*                         GetDimensionCount()                          */
    1676             : /************************************************************************/
    1677             : 
    1678             : /** Return the number of dimensions.
    1679             :  *
    1680             :  * Default implementation is GetDimensions().size(), and may be overridden by
    1681             :  * drivers if they have a faster / less expensive implementations.
    1682             :  *
    1683             :  * This is the same as the C function GDALMDArrayGetDimensionCount() or
    1684             :  * GDALAttributeGetDimensionCount().
    1685             :  *
    1686             :  */
    1687       57638 : size_t GDALAbstractMDArray::GetDimensionCount() const
    1688             : {
    1689       57638 :     return GetDimensions().size();
    1690             : }
    1691             : 
    1692             : /************************************************************************/
    1693             : /*                               Rename()                               */
    1694             : /************************************************************************/
    1695             : 
    1696             : /** Rename the attribute/array.
    1697             :  *
    1698             :  * This is not implemented by all drivers.
    1699             :  *
    1700             :  * Drivers known to implement it: MEM, netCDF, Zarr.
    1701             :  *
    1702             :  * This is the same as the C functions GDALMDArrayRename() or
    1703             :  * GDALAttributeRename().
    1704             :  *
    1705             :  * @param osNewName New name.
    1706             :  *
    1707             :  * @return true in case of success
    1708             :  * @since GDAL 3.8
    1709             :  */
    1710           0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
    1711             : {
    1712           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
    1713           0 :     return false;
    1714             : }
    1715             : 
    1716             : /************************************************************************/
    1717             : /*                             CopyValue()                              */
    1718             : /************************************************************************/
    1719             : 
    1720             : /** Convert a value from a source type to a destination type.
    1721             :  *
    1722             :  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
    1723             :  * that must be freed with CPLFree().
    1724             :  */
    1725      611925 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
    1726             :                                      const GDALExtendedDataType &srcType,
    1727             :                                      void *pDst,
    1728             :                                      const GDALExtendedDataType &dstType)
    1729             : {
    1730     1215320 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1731      603397 :         dstType.GetClass() == GEDTC_NUMERIC)
    1732             :     {
    1733      601150 :         GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
    1734             :                         dstType.GetNumericDataType(), 0, 1);
    1735      601150 :         return true;
    1736             :     }
    1737       18911 :     if (srcType.GetClass() == GEDTC_STRING &&
    1738        8136 :         dstType.GetClass() == GEDTC_STRING)
    1739             :     {
    1740             :         const char *srcStrPtr;
    1741        6886 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
    1742        6886 :         char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
    1743        6886 :         *reinterpret_cast<void **>(pDst) = pszDup;
    1744        6886 :         return true;
    1745             :     }
    1746        6136 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1747        2247 :         dstType.GetClass() == GEDTC_STRING)
    1748             :     {
    1749        2247 :         const char *str = nullptr;
    1750        2247 :         switch (srcType.GetNumericDataType())
    1751             :         {
    1752           0 :             case GDT_Unknown:
    1753           0 :                 break;
    1754         531 :             case GDT_UInt8:
    1755         531 :                 str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
    1756         531 :                 break;
    1757           3 :             case GDT_Int8:
    1758           3 :                 str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
    1759           3 :                 break;
    1760          96 :             case GDT_UInt16:
    1761          96 :                 str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
    1762          96 :                 break;
    1763           0 :             case GDT_Int16:
    1764           0 :                 str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
    1765           0 :                 break;
    1766         153 :             case GDT_UInt32:
    1767         153 :                 str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
    1768         153 :                 break;
    1769          68 :             case GDT_Int32:
    1770          68 :                 str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
    1771          68 :                 break;
    1772           0 :             case GDT_UInt64:
    1773             :                 str =
    1774           0 :                     CPLSPrintf(CPL_FRMT_GUIB,
    1775             :                                static_cast<GUIntBig>(
    1776             :                                    *static_cast<const std::uint64_t *>(pSrc)));
    1777           0 :                 break;
    1778          53 :             case GDT_Int64:
    1779          53 :                 str = CPLSPrintf(CPL_FRMT_GIB,
    1780             :                                  static_cast<GIntBig>(
    1781             :                                      *static_cast<const std::int64_t *>(pSrc)));
    1782          53 :                 break;
    1783           0 :             case GDT_Float16:
    1784           0 :                 str = CPLSPrintf("%.5g",
    1785             :                                  double(*static_cast<const GFloat16 *>(pSrc)));
    1786           0 :                 break;
    1787         449 :             case GDT_Float32:
    1788         898 :                 str = CPLSPrintf(
    1789             :                     "%.9g",
    1790         449 :                     static_cast<double>(*static_cast<const float *>(pSrc)));
    1791         449 :                 break;
    1792         892 :             case GDT_Float64:
    1793         892 :                 str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
    1794         892 :                 break;
    1795           2 :             case GDT_CInt16:
    1796             :             {
    1797           2 :                 const GInt16 *src = static_cast<const GInt16 *>(pSrc);
    1798           2 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
    1799           2 :                 break;
    1800             :             }
    1801           0 :             case GDT_CInt32:
    1802             :             {
    1803           0 :                 const GInt32 *src = static_cast<const GInt32 *>(pSrc);
    1804           0 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
    1805           0 :                 break;
    1806             :             }
    1807           0 :             case GDT_CFloat16:
    1808             :             {
    1809           0 :                 const GFloat16 *src = static_cast<const GFloat16 *>(pSrc);
    1810           0 :                 str = CPLSPrintf("%.5g+%.5gj", double(src[0]), double(src[1]));
    1811           0 :                 break;
    1812             :             }
    1813           0 :             case GDT_CFloat32:
    1814             :             {
    1815           0 :                 const float *src = static_cast<const float *>(pSrc);
    1816           0 :                 str = CPLSPrintf("%.9g+%.9gj", double(src[0]), double(src[1]));
    1817           0 :                 break;
    1818             :             }
    1819           0 :             case GDT_CFloat64:
    1820             :             {
    1821           0 :                 const double *src = static_cast<const double *>(pSrc);
    1822           0 :                 str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
    1823           0 :                 break;
    1824             :             }
    1825           0 :             case GDT_TypeCount:
    1826           0 :                 CPLAssert(false);
    1827             :                 break;
    1828             :         }
    1829        2247 :         char *pszDup = str ? CPLStrdup(str) : nullptr;
    1830        2247 :         *reinterpret_cast<void **>(pDst) = pszDup;
    1831        2247 :         return true;
    1832             :     }
    1833        2892 :     if (srcType.GetClass() == GEDTC_STRING &&
    1834        1250 :         dstType.GetClass() == GEDTC_NUMERIC)
    1835             :     {
    1836             :         const char *srcStrPtr;
    1837        1250 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
    1838        1250 :         if (dstType.GetNumericDataType() == GDT_Int64)
    1839             :         {
    1840           2 :             *(static_cast<int64_t *>(pDst)) =
    1841           2 :                 srcStrPtr == nullptr ? 0
    1842           1 :                                      : static_cast<int64_t>(atoll(srcStrPtr));
    1843             :         }
    1844        1248 :         else if (dstType.GetNumericDataType() == GDT_UInt64)
    1845             :         {
    1846           2 :             *(static_cast<uint64_t *>(pDst)) =
    1847           2 :                 srcStrPtr == nullptr
    1848           2 :                     ? 0
    1849           1 :                     : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
    1850             :         }
    1851             :         else
    1852             :         {
    1853        1246 :             const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
    1854        1246 :             GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
    1855             :                             dstType.GetNumericDataType(), 0, 1);
    1856             :         }
    1857        1250 :         return true;
    1858             :     }
    1859         784 :     if (srcType.GetClass() == GEDTC_COMPOUND &&
    1860         392 :         dstType.GetClass() == GEDTC_COMPOUND)
    1861             :     {
    1862         392 :         const auto &srcComponents = srcType.GetComponents();
    1863         392 :         const auto &dstComponents = dstType.GetComponents();
    1864         392 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
    1865         392 :         GByte *pabyDst = static_cast<GByte *>(pDst);
    1866             : 
    1867             :         std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
    1868         784 :             srcComponentMap;
    1869        2210 :         for (const auto &srcComp : srcComponents)
    1870             :         {
    1871        1818 :             srcComponentMap[srcComp->GetName()] = &srcComp;
    1872             :         }
    1873         954 :         for (const auto &dstComp : dstComponents)
    1874             :         {
    1875         562 :             auto oIter = srcComponentMap.find(dstComp->GetName());
    1876         562 :             if (oIter == srcComponentMap.end())
    1877           0 :                 return false;
    1878         562 :             const auto &srcComp = *(oIter->second);
    1879        1686 :             if (!GDALExtendedDataType::CopyValue(
    1880         562 :                     pabySrc + srcComp->GetOffset(), srcComp->GetType(),
    1881         562 :                     pabyDst + dstComp->GetOffset(), dstComp->GetType()))
    1882             :             {
    1883           0 :                 return false;
    1884             :             }
    1885             :         }
    1886         392 :         return true;
    1887             :     }
    1888             : 
    1889           0 :     return false;
    1890             : }
    1891             : 
    1892             : /************************************************************************/
    1893             : /*                             CopyValues()                             */
    1894             : /************************************************************************/
    1895             : 
    1896             : /** Convert several values from a source type to a destination type.
    1897             :  *
    1898             :  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
    1899             :  * that must be freed with CPLFree().
    1900             :  */
    1901         507 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
    1902             :                                       const GDALExtendedDataType &srcType,
    1903             :                                       GPtrDiff_t nSrcStrideInElts, void *pDst,
    1904             :                                       const GDALExtendedDataType &dstType,
    1905             :                                       GPtrDiff_t nDstStrideInElts,
    1906             :                                       size_t nValues)
    1907             : {
    1908             :     const auto nSrcStrideInBytes =
    1909         507 :         nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
    1910             :     const auto nDstStrideInBytes =
    1911         507 :         nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
    1912         773 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1913         266 :         dstType.GetClass() == GEDTC_NUMERIC &&
    1914         266 :         nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
    1915         266 :         nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
    1916        1039 :         nDstStrideInBytes >= std::numeric_limits<int>::min() &&
    1917         266 :         nDstStrideInBytes <= std::numeric_limits<int>::max())
    1918             :     {
    1919         266 :         GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
    1920             :                         static_cast<int>(nSrcStrideInBytes), pDst,
    1921             :                         dstType.GetNumericDataType(),
    1922             :                         static_cast<int>(nDstStrideInBytes), nValues);
    1923             :     }
    1924             :     else
    1925             :     {
    1926         241 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
    1927         241 :         GByte *pabyDst = static_cast<GByte *>(pDst);
    1928         482 :         for (size_t i = 0; i < nValues; ++i)
    1929             :         {
    1930         241 :             if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
    1931           0 :                 return false;
    1932         241 :             pabySrc += nSrcStrideInBytes;
    1933         241 :             pabyDst += nDstStrideInBytes;
    1934             :         }
    1935             :     }
    1936         507 :     return true;
    1937             : }
    1938             : 
    1939             : /************************************************************************/
    1940             : /*                        CheckReadWriteParams()                        */
    1941             : /************************************************************************/
    1942             : //! @cond Doxygen_Suppress
    1943       22230 : bool GDALAbstractMDArray::CheckReadWriteParams(
    1944             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
    1945             :     const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
    1946             :     const void *buffer, const void *buffer_alloc_start,
    1947             :     size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
    1948             :     std::vector<GPtrDiff_t> &tmp_bufferStride) const
    1949             : {
    1950           0 :     const auto lamda_error = []()
    1951             :     {
    1952           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1953             :                  "Not all elements pointed by buffer will fit in "
    1954             :                  "[buffer_alloc_start, "
    1955             :                  "buffer_alloc_start + buffer_alloc_size]");
    1956           0 :     };
    1957             : 
    1958       22230 :     const auto &dims = GetDimensions();
    1959       22230 :     if (dims.empty())
    1960             :     {
    1961       13644 :         if (buffer_alloc_start)
    1962             :         {
    1963       12772 :             const size_t elementSize = bufferDataType.GetSize();
    1964       12772 :             const GByte *paby_buffer = static_cast<const GByte *>(buffer);
    1965       12772 :             const GByte *paby_buffer_alloc_start =
    1966             :                 static_cast<const GByte *>(buffer_alloc_start);
    1967       12772 :             const GByte *paby_buffer_alloc_end =
    1968             :                 paby_buffer_alloc_start + buffer_alloc_size;
    1969             : 
    1970       12772 :             if (paby_buffer < paby_buffer_alloc_start ||
    1971       12772 :                 paby_buffer + elementSize > paby_buffer_alloc_end)
    1972             :             {
    1973           0 :                 lamda_error();
    1974           0 :                 return false;
    1975             :             }
    1976             :         }
    1977       13644 :         return true;
    1978             :     }
    1979             : 
    1980        8586 :     if (arrayStep == nullptr)
    1981             :     {
    1982        2047 :         tmp_arrayStep.resize(dims.size(), 1);
    1983        2047 :         arrayStep = tmp_arrayStep.data();
    1984             :     }
    1985       23291 :     for (size_t i = 0; i < dims.size(); i++)
    1986             :     {
    1987       14705 :         assert(count);
    1988       14705 :         if (count[i] == 0)
    1989             :         {
    1990           0 :             CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
    1991             :                      static_cast<unsigned>(i));
    1992           0 :             return false;
    1993             :         }
    1994             :     }
    1995        8586 :     bool bufferStride_all_positive = true;
    1996        8586 :     if (bufferStride == nullptr)
    1997             :     {
    1998        1558 :         GPtrDiff_t stride = 1;
    1999        1558 :         assert(dims.empty() || count != nullptr);
    2000             :         // To compute strides we must proceed from the fastest varying dimension
    2001             :         // (the last one), and then reverse the result
    2002        3528 :         for (size_t i = dims.size(); i != 0;)
    2003             :         {
    2004        1970 :             --i;
    2005        1970 :             tmp_bufferStride.push_back(stride);
    2006        1970 :             GUInt64 newStride = 0;
    2007             :             bool bOK;
    2008             :             try
    2009             :             {
    2010        1970 :                 newStride = (CPLSM(static_cast<uint64_t>(stride)) *
    2011        3940 :                              CPLSM(static_cast<uint64_t>(count[i])))
    2012        1970 :                                 .v();
    2013        1970 :                 bOK = static_cast<size_t>(newStride) == newStride &&
    2014        1970 :                       newStride < std::numeric_limits<size_t>::max() / 2;
    2015             :             }
    2016           0 :             catch (...)
    2017             :             {
    2018           0 :                 bOK = false;
    2019             :             }
    2020        1970 :             if (!bOK)
    2021             :             {
    2022           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
    2023           0 :                 return false;
    2024             :             }
    2025        1970 :             stride = static_cast<GPtrDiff_t>(newStride);
    2026             :         }
    2027        1558 :         std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
    2028        1558 :         bufferStride = tmp_bufferStride.data();
    2029             :     }
    2030             :     else
    2031             :     {
    2032       19641 :         for (size_t i = 0; i < dims.size(); i++)
    2033             :         {
    2034       12674 :             if (bufferStride[i] < 0)
    2035             :             {
    2036          61 :                 bufferStride_all_positive = false;
    2037          61 :                 break;
    2038             :             }
    2039             :         }
    2040             :     }
    2041       23260 :     for (size_t i = 0; i < dims.size(); i++)
    2042             :     {
    2043       14685 :         assert(arrayStartIdx);
    2044       14685 :         assert(count);
    2045       14685 :         if (arrayStartIdx[i] >= dims[i]->GetSize())
    2046             :         {
    2047           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    2048             :                      "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
    2049             :                      static_cast<unsigned>(i),
    2050           2 :                      static_cast<GUInt64>(arrayStartIdx[i]),
    2051           2 :                      static_cast<GUInt64>(dims[i]->GetSize()));
    2052           2 :             return false;
    2053             :         }
    2054             :         bool bOverflow;
    2055       14683 :         if (arrayStep[i] >= 0)
    2056             :         {
    2057             :             try
    2058             :             {
    2059       13430 :                 bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
    2060       13432 :                              CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    2061       53723 :                                  CPLSM(static_cast<uint64_t>(arrayStep[i])))
    2062       13430 :                                 .v() >= dims[i]->GetSize();
    2063             :             }
    2064           1 :             catch (...)
    2065             :             {
    2066           1 :                 bOverflow = true;
    2067             :             }
    2068       13431 :             if (bOverflow)
    2069             :             {
    2070           6 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2071             :                          "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
    2072             :                          ">= " CPL_FRMT_GUIB,
    2073             :                          static_cast<unsigned>(i), static_cast<unsigned>(i),
    2074             :                          static_cast<unsigned>(i),
    2075           6 :                          static_cast<GUInt64>(dims[i]->GetSize()));
    2076           6 :                 return false;
    2077             :             }
    2078             :         }
    2079             :         else
    2080             :         {
    2081             :             try
    2082             :             {
    2083        1252 :                 bOverflow =
    2084        1252 :                     arrayStartIdx[i] <
    2085        1252 :                     (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    2086        2504 :                      CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
    2087             :                                ? (static_cast<uint64_t>(1) << 63)
    2088        2504 :                                : static_cast<uint64_t>(-arrayStep[i])))
    2089        1252 :                         .v();
    2090             :             }
    2091           0 :             catch (...)
    2092             :             {
    2093           0 :                 bOverflow = true;
    2094             :             }
    2095        1252 :             if (bOverflow)
    2096             :             {
    2097           3 :                 CPLError(
    2098             :                     CE_Failure, CPLE_AppDefined,
    2099             :                     "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
    2100             :                     static_cast<unsigned>(i), static_cast<unsigned>(i),
    2101             :                     static_cast<unsigned>(i));
    2102           3 :                 return false;
    2103             :             }
    2104             :         }
    2105             :     }
    2106             : 
    2107        8575 :     if (buffer_alloc_start)
    2108             :     {
    2109        4180 :         const size_t elementSize = bufferDataType.GetSize();
    2110        4180 :         const GByte *paby_buffer = static_cast<const GByte *>(buffer);
    2111        4180 :         const GByte *paby_buffer_alloc_start =
    2112             :             static_cast<const GByte *>(buffer_alloc_start);
    2113        4180 :         const GByte *paby_buffer_alloc_end =
    2114             :             paby_buffer_alloc_start + buffer_alloc_size;
    2115        4180 :         if (bufferStride_all_positive)
    2116             :         {
    2117        4180 :             if (paby_buffer < paby_buffer_alloc_start)
    2118             :             {
    2119           0 :                 lamda_error();
    2120           0 :                 return false;
    2121             :             }
    2122        4180 :             GUInt64 nOffset = elementSize;
    2123       11995 :             for (size_t i = 0; i < dims.size(); i++)
    2124             :             {
    2125             :                 try
    2126             :                 {
    2127        7815 :                     nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
    2128        7815 :                                CPLSM(static_cast<uint64_t>(bufferStride[i])) *
    2129       15630 :                                    CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    2130       31260 :                                    CPLSM(static_cast<uint64_t>(elementSize)))
    2131        7815 :                                   .v();
    2132             :                 }
    2133           0 :                 catch (...)
    2134             :                 {
    2135           0 :                     lamda_error();
    2136           0 :                     return false;
    2137             :                 }
    2138             :             }
    2139             : #if SIZEOF_VOIDP == 4
    2140             :             if (static_cast<size_t>(nOffset) != nOffset)
    2141             :             {
    2142             :                 lamda_error();
    2143             :                 return false;
    2144             :             }
    2145             : #endif
    2146        4180 :             if (paby_buffer + nOffset > paby_buffer_alloc_end)
    2147             :             {
    2148           0 :                 lamda_error();
    2149           0 :                 return false;
    2150             :             }
    2151             :         }
    2152           0 :         else if (dims.size() < 31)
    2153             :         {
    2154             :             // Check all corners of the hypercube
    2155           0 :             const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
    2156           0 :             for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
    2157             :             {
    2158           0 :                 const GByte *paby = paby_buffer;
    2159           0 :                 for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
    2160             :                      i++)
    2161             :                 {
    2162           0 :                     if (iCornerCode & (1U << i))
    2163             :                     {
    2164             :                         // We should check for integer overflows
    2165           0 :                         paby += bufferStride[i] * (count[i] - 1) * elementSize;
    2166             :                     }
    2167             :                 }
    2168           0 :                 if (paby < paby_buffer_alloc_start ||
    2169           0 :                     paby + elementSize > paby_buffer_alloc_end)
    2170             :                 {
    2171           0 :                     lamda_error();
    2172           0 :                     return false;
    2173             :                 }
    2174             :             }
    2175             :         }
    2176             :     }
    2177             : 
    2178        8575 :     return true;
    2179             : }
    2180             : 
    2181             : //! @endcond
    2182             : 
    2183             : /************************************************************************/
    2184             : /*                                Read()                                */
    2185             : /************************************************************************/
    2186             : 
    2187             : /** Read part or totality of a multidimensional array or attribute.
    2188             :  *
    2189             :  * This will extract the content of a hyper-rectangle from the array into
    2190             :  * a user supplied buffer.
    2191             :  *
    2192             :  * If bufferDataType is of type string, the values written in pDstBuffer
    2193             :  * will be char* pointers and the strings should be freed with CPLFree().
    2194             :  *
    2195             :  * This is the same as the C function GDALMDArrayRead().
    2196             :  *
    2197             :  * @param arrayStartIdx Values representing the starting index to read
    2198             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2199             :  *                      Array of GetDimensionCount() values. Must not be
    2200             :  *                      nullptr, unless for a zero-dimensional array.
    2201             :  *
    2202             :  * @param count         Values representing the number of values to extract in
    2203             :  *                      each dimension.
    2204             :  *                      Array of GetDimensionCount() values. Must not be
    2205             :  *                      nullptr, unless for a zero-dimensional array.
    2206             :  *
    2207             :  * @param arrayStep     Spacing between values to extract in each dimension.
    2208             :  *                      The spacing is in number of array elements, not bytes.
    2209             :  *                      If provided, must contain GetDimensionCount() values.
    2210             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
    2211             :  * default to indicate consecutive elements.
    2212             :  *
    2213             :  * @param bufferStride  Spacing between values to store in pDstBuffer.
    2214             :  *                      The spacing is in number of array elements, not bytes.
    2215             :  *                      If provided, must contain GetDimensionCount() values.
    2216             :  *                      Negative values are possible (for example to reorder
    2217             :  *                      from bottom-to-top to top-to-bottom).
    2218             :  *                      If set to nullptr, will be set so that pDstBuffer is
    2219             :  *                      written in a compact way, with elements of the last /
    2220             :  *                      fastest varying dimension being consecutive.
    2221             :  *
    2222             :  * @param bufferDataType Data type of values in pDstBuffer.
    2223             :  *
    2224             :  * @param pDstBuffer    User buffer to store the values read. Should be big
    2225             :  *                      enough to store the number of values indicated by
    2226             :  * count[] and with the spacing of bufferStride[].
    2227             :  *
    2228             :  * @param pDstBufferAllocStart Optional pointer that can be used to validate the
    2229             :  *                             validity of pDstBuffer. pDstBufferAllocStart
    2230             :  * should be the pointer returned by the malloc() or equivalent call used to
    2231             :  * allocate the buffer. It will generally be equal to pDstBuffer (when
    2232             :  * bufferStride[] values are all positive), but not necessarily. If specified,
    2233             :  * nDstBufferAllocSize should be also set to the appropriate value. If no
    2234             :  * validation is needed, nullptr can be passed.
    2235             :  *
    2236             :  * @param nDstBufferAllocSize  Optional buffer size, that can be used to
    2237             :  * validate the validity of pDstBuffer. This is the size of the buffer starting
    2238             :  * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
    2239             :  *                             set to the appropriate value.
    2240             :  *                             If no validation is needed, 0 can be passed.
    2241             :  *
    2242             :  * @return true in case of success.
    2243             :  */
    2244       12446 : bool GDALAbstractMDArray::Read(
    2245             :     const GUInt64 *arrayStartIdx, const size_t *count,
    2246             :     const GInt64 *arrayStep,         // step in elements
    2247             :     const GPtrDiff_t *bufferStride,  // stride in elements
    2248             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    2249             :     const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
    2250             : {
    2251       12446 :     if (!GetDataType().CanConvertTo(bufferDataType))
    2252             :     {
    2253           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2254             :                  "Array data type is not convertible to buffer data type");
    2255           0 :         return false;
    2256             :     }
    2257             : 
    2258       24892 :     std::vector<GInt64> tmp_arrayStep;
    2259       24892 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    2260       12446 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    2261             :                               bufferDataType, pDstBuffer, pDstBufferAllocStart,
    2262             :                               nDstBufferAllocSize, tmp_arrayStep,
    2263             :                               tmp_bufferStride))
    2264             :     {
    2265           0 :         return false;
    2266             :     }
    2267             : 
    2268       12446 :     return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
    2269       12446 :                  pDstBuffer);
    2270             : }
    2271             : 
    2272             : /************************************************************************/
    2273             : /*                               IWrite()                               */
    2274             : /************************************************************************/
    2275             : 
    2276             : //! @cond Doxygen_Suppress
    2277           1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
    2278             :                                  const GInt64 *, const GPtrDiff_t *,
    2279             :                                  const GDALExtendedDataType &, const void *)
    2280             : {
    2281           1 :     CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
    2282           1 :     return false;
    2283             : }
    2284             : 
    2285             : //! @endcond
    2286             : 
    2287             : /************************************************************************/
    2288             : /*                               Write()                                */
    2289             : /************************************************************************/
    2290             : 
    2291             : /** Write part or totality of a multidimensional array or attribute.
    2292             :  *
    2293             :  * This will set the content of a hyper-rectangle into the array from
    2294             :  * a user supplied buffer.
    2295             :  *
    2296             :  * If bufferDataType is of type string, the values read from pSrcBuffer
    2297             :  * will be char* pointers.
    2298             :  *
    2299             :  * This is the same as the C function GDALMDArrayWrite().
    2300             :  *
    2301             :  * @param arrayStartIdx Values representing the starting index to write
    2302             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2303             :  *                      Array of GetDimensionCount() values. Must not be
    2304             :  *                      nullptr, unless for a zero-dimensional array.
    2305             :  *
    2306             :  * @param count         Values representing the number of values to write in
    2307             :  *                      each dimension.
    2308             :  *                      Array of GetDimensionCount() values. Must not be
    2309             :  *                      nullptr, unless for a zero-dimensional array.
    2310             :  *
    2311             :  * @param arrayStep     Spacing between values to write in each dimension.
    2312             :  *                      The spacing is in number of array elements, not bytes.
    2313             :  *                      If provided, must contain GetDimensionCount() values.
    2314             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
    2315             :  * default to indicate consecutive elements.
    2316             :  *
    2317             :  * @param bufferStride  Spacing between values to read from pSrcBuffer.
    2318             :  *                      The spacing is in number of array elements, not bytes.
    2319             :  *                      If provided, must contain GetDimensionCount() values.
    2320             :  *                      Negative values are possible (for example to reorder
    2321             :  *                      from bottom-to-top to top-to-bottom).
    2322             :  *                      If set to nullptr, will be set so that pSrcBuffer is
    2323             :  *                      written in a compact way, with elements of the last /
    2324             :  *                      fastest varying dimension being consecutive.
    2325             :  *
    2326             :  * @param bufferDataType Data type of values in pSrcBuffer.
    2327             :  *
    2328             :  * @param pSrcBuffer    User buffer to read the values from. Should be big
    2329             :  *                      enough to store the number of values indicated by
    2330             :  * count[] and with the spacing of bufferStride[].
    2331             :  *
    2332             :  * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
    2333             :  *                             validity of pSrcBuffer. pSrcBufferAllocStart
    2334             :  * should be the pointer returned by the malloc() or equivalent call used to
    2335             :  * allocate the buffer. It will generally be equal to pSrcBuffer (when
    2336             :  * bufferStride[] values are all positive), but not necessarily. If specified,
    2337             :  * nSrcBufferAllocSize should be also set to the appropriate value. If no
    2338             :  * validation is needed, nullptr can be passed.
    2339             :  *
    2340             :  * @param nSrcBufferAllocSize  Optional buffer size, that can be used to
    2341             :  * validate the validity of pSrcBuffer. This is the size of the buffer starting
    2342             :  * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
    2343             :  *                             set to the appropriate value.
    2344             :  *                             If no validation is needed, 0 can be passed.
    2345             :  *
    2346             :  * @return true in case of success.
    2347             :  */
    2348        3280 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
    2349             :                                 const size_t *count, const GInt64 *arrayStep,
    2350             :                                 const GPtrDiff_t *bufferStride,
    2351             :                                 const GDALExtendedDataType &bufferDataType,
    2352             :                                 const void *pSrcBuffer,
    2353             :                                 const void *pSrcBufferAllocStart,
    2354             :                                 size_t nSrcBufferAllocSize)
    2355             : {
    2356        3280 :     if (!bufferDataType.CanConvertTo(GetDataType()))
    2357             :     {
    2358           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2359             :                  "Buffer data type is not convertible to array data type");
    2360           0 :         return false;
    2361             :     }
    2362             : 
    2363        6560 :     std::vector<GInt64> tmp_arrayStep;
    2364        6560 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    2365        3280 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    2366             :                               bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
    2367             :                               nSrcBufferAllocSize, tmp_arrayStep,
    2368             :                               tmp_bufferStride))
    2369             :     {
    2370           0 :         return false;
    2371             :     }
    2372             : 
    2373        3280 :     return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
    2374        3280 :                   pSrcBuffer);
    2375             : }
    2376             : 
    2377             : /************************************************************************/
    2378             : /*                       GetTotalElementsCount()                        */
    2379             : /************************************************************************/
    2380             : 
    2381             : /** Return the total number of values in the array.
    2382             :  *
    2383             :  * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
    2384             :  * and GDALAttributeGetTotalElementsCount().
    2385             :  *
    2386             :  */
    2387        1596 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
    2388             : {
    2389        1596 :     const auto &dims = GetDimensions();
    2390        1596 :     if (dims.empty())
    2391         815 :         return 1;
    2392         781 :     GUInt64 nElts = 1;
    2393        1700 :     for (const auto &dim : dims)
    2394             :     {
    2395             :         try
    2396             :         {
    2397         919 :             nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
    2398        2757 :                      CPLSM(static_cast<uint64_t>(dim->GetSize())))
    2399         919 :                         .v();
    2400             :         }
    2401           0 :         catch (...)
    2402             :         {
    2403           0 :             return 0;
    2404             :         }
    2405             :     }
    2406         781 :     return nElts;
    2407             : }
    2408             : 
    2409             : /************************************************************************/
    2410             : /*                            GetBlockSize()                            */
    2411             : /************************************************************************/
    2412             : 
    2413             : /** Return the "natural" block size of the array along all dimensions.
    2414             :  *
    2415             :  * Some drivers might organize the array in tiles/blocks and reading/writing
    2416             :  * aligned on those tile/block boundaries will be more efficient.
    2417             :  *
    2418             :  * The returned number of elements in the vector is the same as
    2419             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
    2420             :  * the natural block size along the considered dimension.
    2421             :  * "Flat" arrays will typically return a vector of values set to 0.
    2422             :  *
    2423             :  * The default implementation will return a vector of values set to 0.
    2424             :  *
    2425             :  * This method is used by GetProcessingChunkSize().
    2426             :  *
    2427             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikely
    2428             :  * theoretical case of a 32-bit platform, this might exceed its size_t
    2429             :  * allocation capabilities.
    2430             :  *
    2431             :  * This is the same as the C function GDALMDArrayGetBlockSize().
    2432             :  *
    2433             :  * @return the block size, in number of elements along each dimension.
    2434             :  */
    2435         312 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
    2436             : {
    2437         312 :     return std::vector<GUInt64>(GetDimensionCount());
    2438             : }
    2439             : 
    2440             : /************************************************************************/
    2441             : /*                       GetProcessingChunkSize()                       */
    2442             : /************************************************************************/
    2443             : 
    2444             : /** \brief Return an optimal chunk size for read/write operations, given the
    2445             :  * natural block size and memory constraints specified.
    2446             :  *
    2447             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
    2448             :  * multiple of those returned by GetBlockSize() (unless the block define by
    2449             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
    2450             :  * returned by this method).
    2451             :  *
    2452             :  * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
    2453             :  *
    2454             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
    2455             :  * chunk.
    2456             :  *
    2457             :  * @return the chunk size, in number of elements along each dimension.
    2458             :  */
    2459             : std::vector<size_t>
    2460          99 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
    2461             : {
    2462          99 :     const auto &dims = GetDimensions();
    2463          99 :     const auto &nDTSize = GetDataType().GetSize();
    2464          99 :     std::vector<size_t> anChunkSize;
    2465         198 :     auto blockSize = GetBlockSize();
    2466          99 :     CPLAssert(blockSize.size() == dims.size());
    2467          99 :     size_t nChunkSize = nDTSize;
    2468          99 :     bool bOverflow = false;
    2469          99 :     constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
    2470             :     // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
    2471             :     // [1, min(sizet_max, dim_size[i])]
    2472             :     // Also make sure that the product of all anChunkSize[i]) fits on size_t
    2473         268 :     for (size_t i = 0; i < dims.size(); i++)
    2474             :     {
    2475             :         const auto sizeDimI =
    2476         338 :             std::max(static_cast<size_t>(1),
    2477         338 :                      static_cast<size_t>(
    2478         338 :                          std::min(static_cast<GUInt64>(kSIZE_T_MAX),
    2479         169 :                                   std::min(blockSize[i], dims[i]->GetSize()))));
    2480         169 :         anChunkSize.push_back(sizeDimI);
    2481         169 :         if (nChunkSize > kSIZE_T_MAX / sizeDimI)
    2482             :         {
    2483           4 :             bOverflow = true;
    2484             :         }
    2485             :         else
    2486             :         {
    2487         165 :             nChunkSize *= sizeDimI;
    2488             :         }
    2489             :     }
    2490          99 :     if (nChunkSize == 0)
    2491           0 :         return anChunkSize;
    2492             : 
    2493             :     // If the product of all anChunkSize[i] does not fit on size_t, then
    2494             :     // set lowest anChunkSize[i] to 1.
    2495          99 :     if (bOverflow)
    2496             :     {
    2497           2 :         nChunkSize = nDTSize;
    2498           2 :         bOverflow = false;
    2499           8 :         for (size_t i = dims.size(); i > 0;)
    2500             :         {
    2501           6 :             --i;
    2502           6 :             if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
    2503             :             {
    2504           4 :                 bOverflow = true;
    2505           4 :                 anChunkSize[i] = 1;
    2506             :             }
    2507             :             else
    2508             :             {
    2509           2 :                 nChunkSize *= anChunkSize[i];
    2510             :             }
    2511             :         }
    2512             :     }
    2513             : 
    2514          99 :     nChunkSize = nDTSize;
    2515         198 :     std::vector<size_t> anAccBlockSizeFromStart;
    2516         268 :     for (size_t i = 0; i < dims.size(); i++)
    2517             :     {
    2518         169 :         nChunkSize *= anChunkSize[i];
    2519         169 :         anAccBlockSizeFromStart.push_back(nChunkSize);
    2520             :     }
    2521          99 :     if (nChunkSize <= nMaxChunkMemory / 2)
    2522             :     {
    2523          95 :         size_t nVoxelsFromEnd = 1;
    2524         256 :         for (size_t i = dims.size(); i > 0;)
    2525             :         {
    2526         161 :             --i;
    2527             :             const auto nCurBlockSize =
    2528         161 :                 anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
    2529         161 :             const auto nMul = nMaxChunkMemory / nCurBlockSize;
    2530         161 :             if (nMul >= 2)
    2531             :             {
    2532         153 :                 const auto nSizeThisDim(dims[i]->GetSize());
    2533             :                 const auto nBlocksThisDim =
    2534         153 :                     DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
    2535         153 :                 anChunkSize[i] = static_cast<size_t>(std::min(
    2536         153 :                     anChunkSize[i] *
    2537         306 :                         std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
    2538         153 :                     nSizeThisDim));
    2539             :             }
    2540         161 :             nVoxelsFromEnd *= anChunkSize[i];
    2541             :         }
    2542             :     }
    2543          99 :     return anChunkSize;
    2544             : }
    2545             : 
    2546             : /************************************************************************/
    2547             : /*                             BaseRename()                             */
    2548             : /************************************************************************/
    2549             : 
    2550             : //! @cond Doxygen_Suppress
    2551          18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
    2552             : {
    2553          18 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
    2554          18 :     m_osFullName += osNewName;
    2555          18 :     m_osName = osNewName;
    2556             : 
    2557          18 :     NotifyChildrenOfRenaming();
    2558          18 : }
    2559             : 
    2560             : //! @endcond
    2561             : 
    2562             : //! @cond Doxygen_Suppress
    2563             : /************************************************************************/
    2564             : /*                           ParentRenamed()                            */
    2565             : /************************************************************************/
    2566             : 
    2567          50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
    2568             : {
    2569          50 :     m_osFullName = osNewParentFullName;
    2570          50 :     m_osFullName += "/";
    2571          50 :     m_osFullName += m_osName;
    2572             : 
    2573          50 :     NotifyChildrenOfRenaming();
    2574          50 : }
    2575             : 
    2576             : //! @endcond
    2577             : 
    2578             : /************************************************************************/
    2579             : /*                              Deleted()                               */
    2580             : /************************************************************************/
    2581             : 
    2582             : //! @cond Doxygen_Suppress
    2583          58 : void GDALAbstractMDArray::Deleted()
    2584             : {
    2585          58 :     m_bValid = false;
    2586             : 
    2587          58 :     NotifyChildrenOfDeletion();
    2588          58 : }
    2589             : 
    2590             : //! @endcond
    2591             : 
    2592             : /************************************************************************/
    2593             : /*                           ParentDeleted()                            */
    2594             : /************************************************************************/
    2595             : 
    2596             : //! @cond Doxygen_Suppress
    2597          30 : void GDALAbstractMDArray::ParentDeleted()
    2598             : {
    2599          30 :     Deleted();
    2600          30 : }
    2601             : 
    2602             : //! @endcond
    2603             : 
    2604             : /************************************************************************/
    2605             : /*                     CheckValidAndErrorOutIfNot()                     */
    2606             : /************************************************************************/
    2607             : 
    2608             : //! @cond Doxygen_Suppress
    2609       10196 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
    2610             : {
    2611       10196 :     if (!m_bValid)
    2612             :     {
    2613          26 :         CPLError(CE_Failure, CPLE_AppDefined,
    2614             :                  "This object has been deleted. No action on it is possible");
    2615             :     }
    2616       10196 :     return m_bValid;
    2617             : }
    2618             : 
    2619             : //! @endcond
    2620             : 
    2621             : /************************************************************************/
    2622             : /*                              SetUnit()                               */
    2623             : /************************************************************************/
    2624             : 
    2625             : /** Set the variable unit.
    2626             :  *
    2627             :  * Values should conform as much as possible with those allowed by
    2628             :  * the NetCDF CF conventions:
    2629             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
    2630             :  * but others might be returned.
    2631             :  *
    2632             :  * Few examples are "meter", "degrees", "second", ...
    2633             :  * Empty value means unknown.
    2634             :  *
    2635             :  * This is the same as the C function GDALMDArraySetUnit()
    2636             :  *
    2637             :  * @note Driver implementation: optionally implemented.
    2638             :  *
    2639             :  * @param osUnit unit name.
    2640             :  * @return true in case of success.
    2641             :  */
    2642           0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
    2643             : {
    2644           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
    2645           0 :     return false;
    2646             : }
    2647             : 
    2648             : /************************************************************************/
    2649             : /*                              GetUnit()                               */
    2650             : /************************************************************************/
    2651             : 
    2652             : /** Return the array unit.
    2653             :  *
    2654             :  * Values should conform as much as possible with those allowed by
    2655             :  * the NetCDF CF conventions:
    2656             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
    2657             :  * but others might be returned.
    2658             :  *
    2659             :  * Few examples are "meter", "degrees", "second", ...
    2660             :  * Empty value means unknown.
    2661             :  *
    2662             :  * This is the same as the C function GDALMDArrayGetUnit()
    2663             :  */
    2664           5 : const std::string &GDALMDArray::GetUnit() const
    2665             : {
    2666           5 :     static const std::string emptyString;
    2667           5 :     return emptyString;
    2668             : }
    2669             : 
    2670             : /************************************************************************/
    2671             : /*                           SetSpatialRef()                            */
    2672             : /************************************************************************/
    2673             : 
    2674             : /** Assign a spatial reference system object to the array.
    2675             :  *
    2676             :  * This is the same as the C function GDALMDArraySetSpatialRef().
    2677             :  */
    2678           0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
    2679             : {
    2680           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
    2681           0 :     return false;
    2682             : }
    2683             : 
    2684             : /************************************************************************/
    2685             : /*                           GetSpatialRef()                            */
    2686             : /************************************************************************/
    2687             : 
    2688             : /** Return the spatial reference system object associated with the array.
    2689             :  *
    2690             :  * This is the same as the C function GDALMDArrayGetSpatialRef().
    2691             :  */
    2692           4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
    2693             : {
    2694           4 :     return nullptr;
    2695             : }
    2696             : 
    2697             : /************************************************************************/
    2698             : /*                         GetRawNoDataValue()                          */
    2699             : /************************************************************************/
    2700             : 
    2701             : /** Return the nodata value as a "raw" value.
    2702             :  *
    2703             :  * The value returned might be nullptr in case of no nodata value. When
    2704             :  * a nodata value is registered, a non-nullptr will be returned whose size in
    2705             :  * bytes is GetDataType().GetSize().
    2706             :  *
    2707             :  * The returned value should not be modified or freed. It is valid until
    2708             :  * the array is destroyed, or the next call to GetRawNoDataValue() or
    2709             :  * SetRawNoDataValue(), or any similar methods.
    2710             :  *
    2711             :  * @note Driver implementation: this method shall be implemented if nodata
    2712             :  * is supported.
    2713             :  *
    2714             :  * This is the same as the C function GDALMDArrayGetRawNoDataValue().
    2715             :  *
    2716             :  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
    2717             :  */
    2718           5 : const void *GDALMDArray::GetRawNoDataValue() const
    2719             : {
    2720           5 :     return nullptr;
    2721             : }
    2722             : 
    2723             : /************************************************************************/
    2724             : /*                       GetNoDataValueAsDouble()                       */
    2725             : /************************************************************************/
    2726             : 
    2727             : /** Return the nodata value as a double.
    2728             :  *
    2729             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
    2730             :  *
    2731             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2732             :  * a nodata value exists and can be converted to double. Might be nullptr.
    2733             :  *
    2734             :  * @return the nodata value as a double. A 0.0 value might also indicate the
    2735             :  * absence of a nodata value or an error in the conversion (*pbHasNoData will be
    2736             :  * set to false then).
    2737             :  */
    2738       23215 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
    2739             : {
    2740       23215 :     const void *pNoData = GetRawNoDataValue();
    2741       23215 :     double dfNoData = 0.0;
    2742       23215 :     const auto &eDT = GetDataType();
    2743       23215 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2744       23215 :     if (ok)
    2745             :     {
    2746       22876 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
    2747             :                         GDT_Float64, 0, 1);
    2748             :     }
    2749       23215 :     if (pbHasNoData)
    2750         521 :         *pbHasNoData = ok;
    2751       23215 :     return dfNoData;
    2752             : }
    2753             : 
    2754             : /************************************************************************/
    2755             : /*                       GetNoDataValueAsInt64()                        */
    2756             : /************************************************************************/
    2757             : 
    2758             : /** Return the nodata value as a Int64.
    2759             :  *
    2760             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2761             :  * a nodata value exists and can be converted to Int64. Might be nullptr.
    2762             :  *
    2763             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
    2764             :  *
    2765             :  * @return the nodata value as a Int64
    2766             :  *
    2767             :  * @since GDAL 3.5
    2768             :  */
    2769          14 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
    2770             : {
    2771          14 :     const void *pNoData = GetRawNoDataValue();
    2772          14 :     int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
    2773          14 :     const auto &eDT = GetDataType();
    2774          14 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2775          14 :     if (ok)
    2776             :     {
    2777          10 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
    2778             :                         GDT_Int64, 0, 1);
    2779             :     }
    2780          14 :     if (pbHasNoData)
    2781          14 :         *pbHasNoData = ok;
    2782          14 :     return nNoData;
    2783             : }
    2784             : 
    2785             : /************************************************************************/
    2786             : /*                       GetNoDataValueAsUInt64()                       */
    2787             : /************************************************************************/
    2788             : 
    2789             : /** Return the nodata value as a UInt64.
    2790             :  *
    2791             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
    2792             : 
    2793             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2794             :  * a nodata value exists and can be converted to UInt64. Might be nullptr.
    2795             :  *
    2796             :  * @return the nodata value as a UInt64
    2797             :  *
    2798             :  * @since GDAL 3.5
    2799             :  */
    2800           8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
    2801             : {
    2802           8 :     const void *pNoData = GetRawNoDataValue();
    2803           8 :     uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
    2804           8 :     const auto &eDT = GetDataType();
    2805           8 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2806           8 :     if (ok)
    2807             :     {
    2808           6 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
    2809             :                         GDT_UInt64, 0, 1);
    2810             :     }
    2811           8 :     if (pbHasNoData)
    2812           8 :         *pbHasNoData = ok;
    2813           8 :     return nNoData;
    2814             : }
    2815             : 
    2816             : /************************************************************************/
    2817             : /*                         SetRawNoDataValue()                          */
    2818             : /************************************************************************/
    2819             : 
    2820             : /** Set the nodata value as a "raw" value.
    2821             :  *
    2822             :  * The value passed might be nullptr in case of no nodata value. When
    2823             :  * a nodata value is registered, a non-nullptr whose size in
    2824             :  * bytes is GetDataType().GetSize() must be passed.
    2825             :  *
    2826             :  * This is the same as the C function GDALMDArraySetRawNoDataValue().
    2827             :  *
    2828             :  * @note Driver implementation: this method shall be implemented if setting
    2829             :  nodata
    2830             :  * is supported.
    2831             : 
    2832             :  * @return true in case of success.
    2833             :  */
    2834           0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
    2835             : {
    2836           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2837             :              "SetRawNoDataValue() not implemented");
    2838           0 :     return false;
    2839             : }
    2840             : 
    2841             : /************************************************************************/
    2842             : /*                           SetNoDataValue()                           */
    2843             : /************************************************************************/
    2844             : 
    2845             : /** Set the nodata value as a double.
    2846             :  *
    2847             :  * If the natural data type of the attribute/array is not double, type
    2848             :  * conversion will occur to the type returned by GetDataType().
    2849             :  *
    2850             :  * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
    2851             :  *
    2852             :  * @return true in case of success.
    2853             :  */
    2854          64 : bool GDALMDArray::SetNoDataValue(double dfNoData)
    2855             : {
    2856          64 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2857          64 :     bool bRet = false;
    2858          64 :     if (GDALExtendedDataType::CopyValue(
    2859         128 :             &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
    2860          64 :             GetDataType()))
    2861             :     {
    2862          64 :         bRet = SetRawNoDataValue(pRawNoData);
    2863             :     }
    2864          64 :     CPLFree(pRawNoData);
    2865          64 :     return bRet;
    2866             : }
    2867             : 
    2868             : /************************************************************************/
    2869             : /*                           SetNoDataValue()                           */
    2870             : /************************************************************************/
    2871             : 
    2872             : /** Set the nodata value as a Int64.
    2873             :  *
    2874             :  * If the natural data type of the attribute/array is not Int64, type conversion
    2875             :  * will occur to the type returned by GetDataType().
    2876             :  *
    2877             :  * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
    2878             :  *
    2879             :  * @return true in case of success.
    2880             :  *
    2881             :  * @since GDAL 3.5
    2882             :  */
    2883           6 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
    2884             : {
    2885           6 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2886           6 :     bool bRet = false;
    2887           6 :     if (GDALExtendedDataType::CopyValue(&nNoData,
    2888          12 :                                         GDALExtendedDataType::Create(GDT_Int64),
    2889           6 :                                         pRawNoData, GetDataType()))
    2890             :     {
    2891           6 :         bRet = SetRawNoDataValue(pRawNoData);
    2892             :     }
    2893           6 :     CPLFree(pRawNoData);
    2894           6 :     return bRet;
    2895             : }
    2896             : 
    2897             : /************************************************************************/
    2898             : /*                           SetNoDataValue()                           */
    2899             : /************************************************************************/
    2900             : 
    2901             : /** Set the nodata value as a Int64.
    2902             :  *
    2903             :  * If the natural data type of the attribute/array is not Int64, type conversion
    2904             :  * will occur to the type returned by GetDataType().
    2905             :  *
    2906             :  * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
    2907             :  *
    2908             :  * @return true in case of success.
    2909             :  *
    2910             :  * @since GDAL 3.5
    2911             :  */
    2912           1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
    2913             : {
    2914           1 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2915           1 :     bool bRet = false;
    2916           1 :     if (GDALExtendedDataType::CopyValue(
    2917           2 :             &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
    2918           1 :             GetDataType()))
    2919             :     {
    2920           1 :         bRet = SetRawNoDataValue(pRawNoData);
    2921             :     }
    2922           1 :     CPLFree(pRawNoData);
    2923           1 :     return bRet;
    2924             : }
    2925             : 
    2926             : /************************************************************************/
    2927             : /*                               Resize()                               */
    2928             : /************************************************************************/
    2929             : 
    2930             : /** Resize an array to new dimensions.
    2931             :  *
    2932             :  * Not all drivers may allow this operation, and with restrictions (e.g.
    2933             :  * for netCDF, this is limited to growing of "unlimited" dimensions)
    2934             :  *
    2935             :  * Resizing a dimension used in other arrays will cause those other arrays
    2936             :  * to be resized.
    2937             :  *
    2938             :  * This is the same as the C function GDALMDArrayResize().
    2939             :  *
    2940             :  * @param anNewDimSizes Array of GetDimensionCount() values containing the
    2941             :  *                      new size of each indexing dimension.
    2942             :  * @param papszOptions Options. (Driver specific)
    2943             :  * @return true in case of success.
    2944             :  * @since GDAL 3.7
    2945             :  */
    2946           0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
    2947             :                          CPL_UNUSED CSLConstList papszOptions)
    2948             : {
    2949           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2950             :              "Resize() is not supported for this array");
    2951           0 :     return false;
    2952             : }
    2953             : 
    2954             : /************************************************************************/
    2955             : /*                              SetScale()                              */
    2956             : /************************************************************************/
    2957             : 
    2958             : /** Set the scale value to apply to raw values.
    2959             :  *
    2960             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2961             :  *
    2962             :  * This is the same as the C function GDALMDArraySetScale() /
    2963             :  * GDALMDArraySetScaleEx().
    2964             :  *
    2965             :  * @note Driver implementation: this method shall be implemented if setting
    2966             :  * scale is supported.
    2967             :  *
    2968             :  * @param dfScale scale
    2969             :  * @param eStorageType Data type to which create the potential attribute that
    2970             :  * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
    2971             :  * implementation will decide automatically the data type. Note that changing
    2972             :  * the data type after initial setting might not be supported.
    2973             :  * @return true in case of success.
    2974             :  */
    2975           0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
    2976             :                            CPL_UNUSED GDALDataType eStorageType)
    2977             : {
    2978           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
    2979           0 :     return false;
    2980             : }
    2981             : 
    2982             : /************************************************************************/
    2983             : /*                              SetOffset)                              */
    2984             : /************************************************************************/
    2985             : 
    2986             : /** Set the offset value to apply to raw values.
    2987             :  *
    2988             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2989             :  *
    2990             :  * This is the same as the C function GDALMDArraySetOffset() /
    2991             :  * GDALMDArraySetOffsetEx().
    2992             :  *
    2993             :  * @note Driver implementation: this method shall be implemented if setting
    2994             :  * offset is supported.
    2995             :  *
    2996             :  * @param dfOffset Offset
    2997             :  * @param eStorageType Data type to which create the potential attribute that
    2998             :  * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
    2999             :  * implementation will decide automatically the data type. Note that changing
    3000             :  * the data type after initial setting might not be supported.
    3001             :  * @return true in case of success.
    3002             :  */
    3003           0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
    3004             :                             CPL_UNUSED GDALDataType eStorageType)
    3005             : {
    3006           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
    3007           0 :     return false;
    3008             : }
    3009             : 
    3010             : /************************************************************************/
    3011             : /*                              GetScale()                              */
    3012             : /************************************************************************/
    3013             : 
    3014             : /** Get the scale value to apply to raw values.
    3015             :  *
    3016             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    3017             :  *
    3018             :  * This is the same as the C function GDALMDArrayGetScale().
    3019             :  *
    3020             :  * @note Driver implementation: this method shall be implemented if getting
    3021             :  * scale is supported.
    3022             :  *
    3023             :  * @param pbHasScale Pointer to a output boolean that will be set to true if
    3024             :  * a scale value exists. Might be nullptr.
    3025             :  * @param peStorageType Pointer to a output GDALDataType that will be set to
    3026             :  * the storage type of the scale value, when known/relevant. Otherwise will be
    3027             :  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
    3028             :  *
    3029             :  * @return the scale value. A 1.0 value might also indicate the
    3030             :  * absence of a scale value.
    3031             :  */
    3032          22 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
    3033             :                              CPL_UNUSED GDALDataType *peStorageType) const
    3034             : {
    3035          22 :     if (pbHasScale)
    3036          22 :         *pbHasScale = false;
    3037          22 :     return 1.0;
    3038             : }
    3039             : 
    3040             : /************************************************************************/
    3041             : /*                             GetOffset()                              */
    3042             : /************************************************************************/
    3043             : 
    3044             : /** Get the offset value to apply to raw values.
    3045             :  *
    3046             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    3047             :  *
    3048             :  * This is the same as the C function GDALMDArrayGetOffset().
    3049             :  *
    3050             :  * @note Driver implementation: this method shall be implemented if getting
    3051             :  * offset is supported.
    3052             :  *
    3053             :  * @param pbHasOffset Pointer to a output boolean that will be set to true if
    3054             :  * a offset value exists. Might be nullptr.
    3055             :  * @param peStorageType Pointer to a output GDALDataType that will be set to
    3056             :  * the storage type of the offset value, when known/relevant. Otherwise will be
    3057             :  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
    3058             :  *
    3059             :  * @return the offset value. A 0.0 value might also indicate the
    3060             :  * absence of a offset value.
    3061             :  */
    3062          22 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
    3063             :                               CPL_UNUSED GDALDataType *peStorageType) const
    3064             : {
    3065          22 :     if (pbHasOffset)
    3066          22 :         *pbHasOffset = false;
    3067          22 :     return 0.0;
    3068             : }
    3069             : 
    3070             : /************************************************************************/
    3071             : /*                          ProcessPerChunk()                           */
    3072             : /************************************************************************/
    3073             : 
    3074             : namespace
    3075             : {
    3076             : enum class Caller
    3077             : {
    3078             :     CALLER_END_OF_LOOP,
    3079             :     CALLER_IN_LOOP,
    3080             : };
    3081             : }
    3082             : 
    3083             : /** \brief Call a user-provided function to operate on an array chunk by chunk.
    3084             :  *
    3085             :  * This method is to be used when doing operations on an array, or a subset of
    3086             :  * it, in a chunk by chunk way.
    3087             :  *
    3088             :  * @param arrayStartIdx Values representing the starting index to use
    3089             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    3090             :  *                      Array of GetDimensionCount() values. Must not be
    3091             :  *                      nullptr, unless for a zero-dimensional array.
    3092             :  *
    3093             :  * @param count         Values representing the number of values to use in
    3094             :  *                      each dimension.
    3095             :  *                      Array of GetDimensionCount() values. Must not be
    3096             :  *                      nullptr, unless for a zero-dimensional array.
    3097             :  *
    3098             :  * @param chunkSize     Values representing the chunk size in each dimension.
    3099             :  *                      Might typically the output of GetProcessingChunkSize().
    3100             :  *                      Array of GetDimensionCount() values. Must not be
    3101             :  *                      nullptr, unless for a zero-dimensional array.
    3102             :  *
    3103             :  * @param pfnFunc       User-provided function of type FuncProcessPerChunkType.
    3104             :  *                      Must NOT be nullptr.
    3105             :  *
    3106             :  * @param pUserData     Pointer to pass as the value of the pUserData argument
    3107             :  * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
    3108             :  *
    3109             :  * @return true in case of success.
    3110             :  */
    3111          97 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
    3112             :                                           const GUInt64 *count,
    3113             :                                           const size_t *chunkSize,
    3114             :                                           FuncProcessPerChunkType pfnFunc,
    3115             :                                           void *pUserData)
    3116             : {
    3117          97 :     const auto &dims = GetDimensions();
    3118          97 :     if (dims.empty())
    3119             :     {
    3120           2 :         return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
    3121             :     }
    3122             : 
    3123             :     // Sanity check
    3124          95 :     size_t nTotalChunkSize = 1;
    3125         241 :     for (size_t i = 0; i < dims.size(); i++)
    3126             :     {
    3127         153 :         const auto nSizeThisDim(dims[i]->GetSize());
    3128         153 :         if (count[i] == 0 || count[i] > nSizeThisDim ||
    3129         151 :             arrayStartIdx[i] > nSizeThisDim - count[i])
    3130             :         {
    3131           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    3132             :                      "Inconsistent arrayStartIdx[] / count[] values "
    3133             :                      "regarding array size");
    3134           4 :             return false;
    3135             :         }
    3136         296 :         if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
    3137         147 :             chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
    3138             :         {
    3139           3 :             CPLError(CE_Failure, CPLE_AppDefined,
    3140             :                      "Inconsistent chunkSize[] values");
    3141           3 :             return false;
    3142             :         }
    3143         146 :         nTotalChunkSize *= chunkSize[i];
    3144             :     }
    3145             : 
    3146          88 :     size_t dimIdx = 0;
    3147         176 :     std::vector<GUInt64> chunkArrayStartIdx(dims.size());
    3148         176 :     std::vector<size_t> chunkCount(dims.size());
    3149             : 
    3150             :     struct Stack
    3151             :     {
    3152             :         GUInt64 nBlockCounter = 0;
    3153             :         GUInt64 nBlocksMinusOne = 0;
    3154             :         size_t first_count = 0;  // only used if nBlocks > 1
    3155             :         Caller return_point = Caller::CALLER_END_OF_LOOP;
    3156             :     };
    3157             : 
    3158         176 :     std::vector<Stack> stack(dims.size());
    3159          88 :     GUInt64 iCurChunk = 0;
    3160          88 :     GUInt64 nChunkCount = 1;
    3161         233 :     for (size_t i = 0; i < dims.size(); i++)
    3162             :     {
    3163         145 :         const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
    3164         145 :         const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
    3165         145 :         stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
    3166         145 :         nChunkCount *= 1 + stack[i].nBlocksMinusOne;
    3167         145 :         if (stack[i].nBlocksMinusOne == 0)
    3168             :         {
    3169         140 :             chunkArrayStartIdx[i] = arrayStartIdx[i];
    3170         140 :             chunkCount[i] = static_cast<size_t>(count[i]);
    3171             :         }
    3172             :         else
    3173             :         {
    3174           5 :             stack[i].first_count = static_cast<size_t>(
    3175           5 :                 (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
    3176             :         }
    3177             :     }
    3178             : 
    3179          88 : lbl_next_depth:
    3180         343 :     if (dimIdx == dims.size())
    3181             :     {
    3182         121 :         ++iCurChunk;
    3183         121 :         if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
    3184             :                      iCurChunk, nChunkCount, pUserData))
    3185             :         {
    3186           3 :             return false;
    3187             :         }
    3188             :     }
    3189             :     else
    3190             :     {
    3191         222 :         if (stack[dimIdx].nBlocksMinusOne != 0)
    3192             :         {
    3193          11 :             stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
    3194          11 :             chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
    3195          11 :             chunkCount[dimIdx] = stack[dimIdx].first_count;
    3196          11 :             stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
    3197             :             while (true)
    3198             :             {
    3199          33 :                 dimIdx++;
    3200          33 :                 goto lbl_next_depth;
    3201          33 :             lbl_return_to_caller_in_loop:
    3202          33 :                 --stack[dimIdx].nBlockCounter;
    3203          33 :                 if (stack[dimIdx].nBlockCounter == 0)
    3204          11 :                     break;
    3205          22 :                 chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    3206          22 :                 chunkCount[dimIdx] = chunkSize[dimIdx];
    3207             :             }
    3208             : 
    3209          11 :             chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    3210          22 :             chunkCount[dimIdx] =
    3211          11 :                 static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
    3212          11 :                                     chunkArrayStartIdx[dimIdx]);
    3213          11 :             stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
    3214             :         }
    3215         222 :         dimIdx++;
    3216         222 :         goto lbl_next_depth;
    3217         216 :     lbl_return_to_caller_end_of_loop:
    3218         216 :         if (dimIdx == 0)
    3219          85 :             goto end;
    3220             :     }
    3221             : 
    3222         249 :     assert(dimIdx > 0);
    3223         249 :     dimIdx--;
    3224             :     // cppcheck-suppress negativeContainerIndex
    3225         249 :     switch (stack[dimIdx].return_point)
    3226             :     {
    3227         216 :         case Caller::CALLER_END_OF_LOOP:
    3228         216 :             goto lbl_return_to_caller_end_of_loop;
    3229          33 :         case Caller::CALLER_IN_LOOP:
    3230          33 :             goto lbl_return_to_caller_in_loop;
    3231             :     }
    3232          85 : end:
    3233          85 :     return true;
    3234             : }
    3235             : 
    3236             : /************************************************************************/
    3237             : /*                           GDALAttribute()                            */
    3238             : /************************************************************************/
    3239             : 
    3240             : //! @cond Doxygen_Suppress
    3241      161798 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
    3242           0 :                              CPL_UNUSED const std::string &osName)
    3243             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
    3244      161798 :     : GDALAbstractMDArray(osParentName, osName)
    3245             : #endif
    3246             : {
    3247      161798 : }
    3248             : 
    3249             : GDALAttribute::~GDALAttribute() = default;
    3250             : 
    3251             : //! @endcond
    3252             : 
    3253             : /************************************************************************/
    3254             : /*                          GetDimensionSize()                          */
    3255             : /************************************************************************/
    3256             : 
    3257             : /** Return the size of the dimensions of the attribute.
    3258             :  *
    3259             :  * This will be an empty array for a scalar (single value) attribute.
    3260             :  *
    3261             :  * This is the same as the C function GDALAttributeGetDimensionsSize().
    3262             :  */
    3263         791 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
    3264             : {
    3265         791 :     const auto &dims = GetDimensions();
    3266         791 :     std::vector<GUInt64> ret;
    3267         791 :     ret.reserve(dims.size());
    3268         974 :     for (const auto &dim : dims)
    3269         183 :         ret.push_back(dim->GetSize());
    3270         791 :     return ret;
    3271             : }
    3272             : 
    3273             : /************************************************************************/
    3274             : /*                           GDALRawResult()                            */
    3275             : /************************************************************************/
    3276             : 
    3277             : //! @cond Doxygen_Suppress
    3278         236 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
    3279         236 :                              size_t nEltCount)
    3280         472 :     : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
    3281         236 :       m_raw(raw)
    3282             : {
    3283         236 : }
    3284             : 
    3285             : //! @endcond
    3286             : 
    3287             : /************************************************************************/
    3288             : /*                           GDALRawResult()                            */
    3289             : /************************************************************************/
    3290             : 
    3291             : /** Move constructor. */
    3292           0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
    3293           0 :     : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
    3294           0 :       m_nSize(other.m_nSize), m_raw(other.m_raw)
    3295             : {
    3296           0 :     other.m_nEltCount = 0;
    3297           0 :     other.m_nSize = 0;
    3298           0 :     other.m_raw = nullptr;
    3299           0 : }
    3300             : 
    3301             : /************************************************************************/
    3302             : /*                               FreeMe()                               */
    3303             : /************************************************************************/
    3304             : 
    3305         236 : void GDALRawResult::FreeMe()
    3306             : {
    3307         236 :     if (m_raw && m_dt.NeedsFreeDynamicMemory())
    3308             :     {
    3309         122 :         GByte *pabyPtr = m_raw;
    3310         122 :         const auto nDTSize(m_dt.GetSize());
    3311         244 :         for (size_t i = 0; i < m_nEltCount; ++i)
    3312             :         {
    3313         122 :             m_dt.FreeDynamicMemory(pabyPtr);
    3314         122 :             pabyPtr += nDTSize;
    3315             :         }
    3316             :     }
    3317         236 :     VSIFree(m_raw);
    3318         236 : }
    3319             : 
    3320             : /************************************************************************/
    3321             : /*                             operator=()                              */
    3322             : /************************************************************************/
    3323             : 
    3324             : /** Move assignment. */
    3325           0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
    3326             : {
    3327           0 :     FreeMe();
    3328           0 :     m_dt = std::move(other.m_dt);
    3329           0 :     m_nEltCount = other.m_nEltCount;
    3330           0 :     m_nSize = other.m_nSize;
    3331           0 :     m_raw = other.m_raw;
    3332           0 :     other.m_nEltCount = 0;
    3333           0 :     other.m_nSize = 0;
    3334           0 :     other.m_raw = nullptr;
    3335           0 :     return *this;
    3336             : }
    3337             : 
    3338             : /************************************************************************/
    3339             : /*                           ~GDALRawResult()                           */
    3340             : /************************************************************************/
    3341             : 
    3342             : /** Destructor. */
    3343         236 : GDALRawResult::~GDALRawResult()
    3344             : {
    3345         236 :     FreeMe();
    3346         236 : }
    3347             : 
    3348             : /************************************************************************/
    3349             : /*                             StealData()                              */
    3350             : /************************************************************************/
    3351             : 
    3352             : //! @cond Doxygen_Suppress
    3353             : /** Return buffer to caller which becomes owner of it.
    3354             :  * Only to be used by GDALAttributeReadAsRaw().
    3355             :  */
    3356           6 : GByte *GDALRawResult::StealData()
    3357             : {
    3358           6 :     GByte *ret = m_raw;
    3359           6 :     m_raw = nullptr;
    3360           6 :     m_nEltCount = 0;
    3361           6 :     m_nSize = 0;
    3362           6 :     return ret;
    3363             : }
    3364             : 
    3365             : //! @endcond
    3366             : 
    3367             : /************************************************************************/
    3368             : /*                             ReadAsRaw()                              */
    3369             : /************************************************************************/
    3370             : 
    3371             : /** Return the raw value of an attribute.
    3372             :  *
    3373             :  *
    3374             :  * This is the same as the C function GDALAttributeReadAsRaw().
    3375             :  */
    3376         236 : GDALRawResult GDALAttribute::ReadAsRaw() const
    3377             : {
    3378         236 :     const auto nEltCount(GetTotalElementsCount());
    3379         236 :     const auto &dt(GetDataType());
    3380         236 :     const auto nDTSize(dt.GetSize());
    3381             :     GByte *res = static_cast<GByte *>(
    3382         236 :         VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
    3383         236 :     if (!res)
    3384           0 :         return GDALRawResult(nullptr, dt, 0);
    3385         236 :     const auto &dims = GetDimensions();
    3386         236 :     const auto nDims = GetDimensionCount();
    3387         472 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3388         472 :     std::vector<size_t> count(1 + nDims);
    3389         262 :     for (size_t i = 0; i < nDims; i++)
    3390             :     {
    3391          26 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3392             :     }
    3393         236 :     if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
    3394         236 :               &res[0], static_cast<size_t>(nEltCount * nDTSize)))
    3395             :     {
    3396           0 :         VSIFree(res);
    3397           0 :         return GDALRawResult(nullptr, dt, 0);
    3398             :     }
    3399         236 :     return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
    3400             : }
    3401             : 
    3402             : /************************************************************************/
    3403             : /*                            ReadAsString()                            */
    3404             : /************************************************************************/
    3405             : 
    3406             : /** Return the value of an attribute as a string.
    3407             :  *
    3408             :  * The returned string should not be freed, and its lifetime does not
    3409             :  * excess a next call to ReadAsString() on the same object, or the deletion
    3410             :  * of the object itself.
    3411             :  *
    3412             :  * This function will only return the first element if there are several.
    3413             :  *
    3414             :  * This is the same as the C function GDALAttributeReadAsString()
    3415             :  *
    3416             :  * @return a string, or nullptr.
    3417             :  */
    3418        4680 : const char *GDALAttribute::ReadAsString() const
    3419             : {
    3420        4680 :     const auto nDims = GetDimensionCount();
    3421        9360 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3422        9360 :     std::vector<size_t> count(1 + nDims, 1);
    3423        4680 :     char *szRet = nullptr;
    3424        4680 :     if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
    3425        4680 :               GDALExtendedDataType::CreateString(), &szRet, &szRet,
    3426       14039 :               sizeof(szRet)) ||
    3427        4679 :         szRet == nullptr)
    3428             :     {
    3429           5 :         return nullptr;
    3430             :     }
    3431        4675 :     m_osCachedVal = szRet;
    3432        4675 :     CPLFree(szRet);
    3433        4675 :     return m_osCachedVal.c_str();
    3434             : }
    3435             : 
    3436             : /************************************************************************/
    3437             : /*                             ReadAsInt()                              */
    3438             : /************************************************************************/
    3439             : 
    3440             : /** Return the value of an attribute as a integer.
    3441             :  *
    3442             :  * This function will only return the first element if there are several.
    3443             :  *
    3444             :  * It can fail if its value can not be converted to integer.
    3445             :  *
    3446             :  * This is the same as the C function GDALAttributeReadAsInt()
    3447             :  *
    3448             :  * @return a integer, or INT_MIN in case of error.
    3449             :  */
    3450        2903 : int GDALAttribute::ReadAsInt() const
    3451             : {
    3452        2903 :     const auto nDims = GetDimensionCount();
    3453        5806 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3454        2903 :     std::vector<size_t> count(1 + nDims, 1);
    3455        2903 :     int nRet = INT_MIN;
    3456        2903 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3457        5806 :          GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
    3458        5806 :     return nRet;
    3459             : }
    3460             : 
    3461             : /************************************************************************/
    3462             : /*                            ReadAsInt64()                             */
    3463             : /************************************************************************/
    3464             : 
    3465             : /** Return the value of an attribute as an int64_t.
    3466             :  *
    3467             :  * This function will only return the first element if there are several.
    3468             :  *
    3469             :  * It can fail if its value can not be converted to long.
    3470             :  *
    3471             :  * This is the same as the C function GDALAttributeReadAsInt64()
    3472             :  *
    3473             :  * @return an int64_t, or INT64_MIN in case of error.
    3474             :  */
    3475         131 : int64_t GDALAttribute::ReadAsInt64() const
    3476             : {
    3477         131 :     const auto nDims = GetDimensionCount();
    3478         262 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3479         131 :     std::vector<size_t> count(1 + nDims, 1);
    3480         131 :     int64_t nRet = INT64_MIN;
    3481         131 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3482         262 :          GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
    3483         262 :     return nRet;
    3484             : }
    3485             : 
    3486             : /************************************************************************/
    3487             : /*                            ReadAsDouble()                            */
    3488             : /************************************************************************/
    3489             : 
    3490             : /** Return the value of an attribute as a double.
    3491             :  *
    3492             :  * This function will only return the first element if there are several.
    3493             :  *
    3494             :  * It can fail if its value can not be converted to double.
    3495             :  *
    3496             :  * This is the same as the C function GDALAttributeReadAsInt()
    3497             :  *
    3498             :  * @return a double value.
    3499             :  */
    3500        4003 : double GDALAttribute::ReadAsDouble() const
    3501             : {
    3502        4003 :     const auto nDims = GetDimensionCount();
    3503        8006 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3504        4003 :     std::vector<size_t> count(1 + nDims, 1);
    3505        4003 :     double dfRet = 0;
    3506        4003 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3507        4003 :          GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
    3508        4003 :          sizeof(dfRet));
    3509        8006 :     return dfRet;
    3510             : }
    3511             : 
    3512             : /************************************************************************/
    3513             : /*                         ReadAsStringArray()                          */
    3514             : /************************************************************************/
    3515             : 
    3516             : /** Return the value of an attribute as an array of strings.
    3517             :  *
    3518             :  * This is the same as the C function GDALAttributeReadAsStringArray()
    3519             :  */
    3520         220 : CPLStringList GDALAttribute::ReadAsStringArray() const
    3521             : {
    3522         220 :     const auto nElts = GetTotalElementsCount();
    3523         220 :     if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
    3524           0 :         return CPLStringList();
    3525             :     char **papszList = static_cast<char **>(
    3526         220 :         VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
    3527         220 :     const auto &dims = GetDimensions();
    3528         220 :     const auto nDims = GetDimensionCount();
    3529         440 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3530         440 :     std::vector<size_t> count(1 + nDims);
    3531         350 :     for (size_t i = 0; i < nDims; i++)
    3532             :     {
    3533         130 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3534             :     }
    3535         220 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3536         220 :          GDALExtendedDataType::CreateString(), papszList, papszList,
    3537         220 :          sizeof(char *) * static_cast<int>(nElts));
    3538         807 :     for (int i = 0; i < static_cast<int>(nElts); i++)
    3539             :     {
    3540         587 :         if (papszList[i] == nullptr)
    3541          13 :             papszList[i] = CPLStrdup("");
    3542             :     }
    3543         220 :     return CPLStringList(papszList);
    3544             : }
    3545             : 
    3546             : /************************************************************************/
    3547             : /*                           ReadAsIntArray()                           */
    3548             : /************************************************************************/
    3549             : 
    3550             : /** Return the value of an attribute as an array of integers.
    3551             :  *
    3552             :  * This is the same as the C function GDALAttributeReadAsIntArray().
    3553             :  */
    3554          18 : std::vector<int> GDALAttribute::ReadAsIntArray() const
    3555             : {
    3556          18 :     const auto nElts = GetTotalElementsCount();
    3557             : #if SIZEOF_VOIDP == 4
    3558             :     if (nElts > static_cast<size_t>(nElts))
    3559             :         return {};
    3560             : #endif
    3561          18 :     std::vector<int> res(static_cast<size_t>(nElts));
    3562          18 :     const auto &dims = GetDimensions();
    3563          18 :     const auto nDims = GetDimensionCount();
    3564          36 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3565          36 :     std::vector<size_t> count(1 + nDims);
    3566          38 :     for (size_t i = 0; i < nDims; i++)
    3567             :     {
    3568          20 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3569             :     }
    3570          18 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3571          36 :          GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
    3572          18 :          res.size() * sizeof(res[0]));
    3573          36 :     return res;
    3574             : }
    3575             : 
    3576             : /************************************************************************/
    3577             : /*                          ReadAsInt64Array()                          */
    3578             : /************************************************************************/
    3579             : 
    3580             : /** Return the value of an attribute as an array of int64_t.
    3581             :  *
    3582             :  * This is the same as the C function GDALAttributeReadAsInt64Array().
    3583             :  */
    3584          90 : std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
    3585             : {
    3586          90 :     const auto nElts = GetTotalElementsCount();
    3587             : #if SIZEOF_VOIDP == 4
    3588             :     if (nElts > static_cast<size_t>(nElts))
    3589             :         return {};
    3590             : #endif
    3591          90 :     std::vector<int64_t> res(static_cast<size_t>(nElts));
    3592          90 :     const auto &dims = GetDimensions();
    3593          90 :     const auto nDims = GetDimensionCount();
    3594         180 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3595         180 :     std::vector<size_t> count(1 + nDims);
    3596         180 :     for (size_t i = 0; i < nDims; i++)
    3597             :     {
    3598          90 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3599             :     }
    3600          90 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3601         180 :          GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
    3602          90 :          res.size() * sizeof(res[0]));
    3603         180 :     return res;
    3604             : }
    3605             : 
    3606             : /************************************************************************/
    3607             : /*                         ReadAsDoubleArray()                          */
    3608             : /************************************************************************/
    3609             : 
    3610             : /** Return the value of an attribute as an array of double.
    3611             :  *
    3612             :  * This is the same as the C function GDALAttributeReadAsDoubleArray().
    3613             :  */
    3614         123 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
    3615             : {
    3616         123 :     const auto nElts = GetTotalElementsCount();
    3617             : #if SIZEOF_VOIDP == 4
    3618             :     if (nElts > static_cast<size_t>(nElts))
    3619             :         return {};
    3620             : #endif
    3621         123 :     std::vector<double> res(static_cast<size_t>(nElts));
    3622         123 :     const auto &dims = GetDimensions();
    3623         123 :     const auto nDims = GetDimensionCount();
    3624         246 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3625         246 :     std::vector<size_t> count(1 + nDims);
    3626         230 :     for (size_t i = 0; i < nDims; i++)
    3627             :     {
    3628         107 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3629             :     }
    3630         123 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3631         246 :          GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
    3632         123 :          res.size() * sizeof(res[0]));
    3633         246 :     return res;
    3634             : }
    3635             : 
    3636             : /************************************************************************/
    3637             : /*                               Write()                                */
    3638             : /************************************************************************/
    3639             : 
    3640             : /** Write an attribute from raw values expressed in GetDataType()
    3641             :  *
    3642             :  * The values should be provided in the type of GetDataType() and there should
    3643             :  * be exactly GetTotalElementsCount() of them.
    3644             :  * If GetDataType() is a string, each value should be a char* pointer.
    3645             :  *
    3646             :  * This is the same as the C function GDALAttributeWriteRaw().
    3647             :  *
    3648             :  * @param pabyValue Buffer of nLen bytes.
    3649             :  * @param nLen Size of pabyValue in bytes. Should be equal to
    3650             :  *             GetTotalElementsCount() * GetDataType().GetSize()
    3651             :  * @return true in case of success.
    3652             :  */
    3653         173 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
    3654             : {
    3655         173 :     if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
    3656             :     {
    3657           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3658             :                  "Length is not of expected value");
    3659           0 :         return false;
    3660             :     }
    3661         173 :     const auto &dims = GetDimensions();
    3662         173 :     const auto nDims = GetDimensionCount();
    3663         346 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3664         346 :     std::vector<size_t> count(1 + nDims);
    3665         199 :     for (size_t i = 0; i < nDims; i++)
    3666             :     {
    3667          26 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3668             :     }
    3669         173 :     return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
    3670         173 :                  pabyValue, pabyValue, nLen);
    3671             : }
    3672             : 
    3673             : /************************************************************************/
    3674             : /*                               Write()                                */
    3675             : /************************************************************************/
    3676             : 
    3677             : /** Write an attribute from a string value.
    3678             :  *
    3679             :  * Type conversion will be performed if needed. If the attribute contains
    3680             :  * multiple values, only the first one will be updated.
    3681             :  *
    3682             :  * This is the same as the C function GDALAttributeWriteString().
    3683             :  *
    3684             :  * @param pszValue Pointer to a string.
    3685             :  * @return true in case of success.
    3686             :  */
    3687         418 : bool GDALAttribute::Write(const char *pszValue)
    3688             : {
    3689         418 :     const auto nDims = GetDimensionCount();
    3690         836 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3691         418 :     std::vector<size_t> count(1 + nDims, 1);
    3692         418 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3693         836 :                  GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
    3694         836 :                  sizeof(pszValue));
    3695             : }
    3696             : 
    3697             : /************************************************************************/
    3698             : /*                              WriteInt()                              */
    3699             : /************************************************************************/
    3700             : 
    3701             : /** Write an attribute from a integer value.
    3702             :  *
    3703             :  * Type conversion will be performed if needed. If the attribute contains
    3704             :  * multiple values, only the first one will be updated.
    3705             :  *
    3706             :  * This is the same as the C function GDALAttributeWriteInt().
    3707             :  *
    3708             :  * @param nVal Value.
    3709             :  * @return true in case of success.
    3710             :  */
    3711          23 : bool GDALAttribute::WriteInt(int nVal)
    3712             : {
    3713          23 :     const auto nDims = GetDimensionCount();
    3714          46 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3715          23 :     std::vector<size_t> count(1 + nDims, 1);
    3716          23 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3717          46 :                  GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
    3718          46 :                  sizeof(nVal));
    3719             : }
    3720             : 
    3721             : /************************************************************************/
    3722             : /*                             WriteInt64()                             */
    3723             : /************************************************************************/
    3724             : 
    3725             : /** Write an attribute from an int64_t value.
    3726             :  *
    3727             :  * Type conversion will be performed if needed. If the attribute contains
    3728             :  * multiple values, only the first one will be updated.
    3729             :  *
    3730             :  * This is the same as the C function GDALAttributeWriteInt().
    3731             :  *
    3732             :  * @param nVal Value.
    3733             :  * @return true in case of success.
    3734             :  */
    3735          14 : bool GDALAttribute::WriteInt64(int64_t nVal)
    3736             : {
    3737          14 :     const auto nDims = GetDimensionCount();
    3738          28 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3739          14 :     std::vector<size_t> count(1 + nDims, 1);
    3740          14 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3741          28 :                  GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
    3742          28 :                  sizeof(nVal));
    3743             : }
    3744             : 
    3745             : /************************************************************************/
    3746             : /*                               Write()                                */
    3747             : /************************************************************************/
    3748             : 
    3749             : /** Write an attribute from a double value.
    3750             :  *
    3751             :  * Type conversion will be performed if needed. If the attribute contains
    3752             :  * multiple values, only the first one will be updated.
    3753             :  *
    3754             :  * This is the same as the C function GDALAttributeWriteDouble().
    3755             :  *
    3756             :  * @param dfVal Value.
    3757             :  * @return true in case of success.
    3758             :  */
    3759          40 : bool GDALAttribute::Write(double dfVal)
    3760             : {
    3761          40 :     const auto nDims = GetDimensionCount();
    3762          80 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3763          40 :     std::vector<size_t> count(1 + nDims, 1);
    3764          40 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3765          80 :                  GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
    3766          80 :                  sizeof(dfVal));
    3767             : }
    3768             : 
    3769             : /************************************************************************/
    3770             : /*                               Write()                                */
    3771             : /************************************************************************/
    3772             : 
    3773             : /** Write an attribute from an array of strings.
    3774             :  *
    3775             :  * Type conversion will be performed if needed.
    3776             :  *
    3777             :  * Exactly GetTotalElementsCount() strings must be provided
    3778             :  *
    3779             :  * This is the same as the C function GDALAttributeWriteStringArray().
    3780             :  *
    3781             :  * @param vals Array of strings.
    3782             :  * @return true in case of success.
    3783             :  */
    3784           9 : bool GDALAttribute::Write(CSLConstList vals)
    3785             : {
    3786           9 :     if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
    3787             :     {
    3788           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3789           1 :         return false;
    3790             :     }
    3791           8 :     const auto nDims = GetDimensionCount();
    3792          16 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3793           8 :     std::vector<size_t> count(1 + nDims);
    3794           8 :     const auto &dims = GetDimensions();
    3795          17 :     for (size_t i = 0; i < nDims; i++)
    3796           9 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3797           8 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3798           8 :                  GDALExtendedDataType::CreateString(), vals, vals,
    3799          16 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
    3800             : }
    3801             : 
    3802             : /************************************************************************/
    3803             : /*                               Write()                                */
    3804             : /************************************************************************/
    3805             : 
    3806             : /** Write an attribute from an array of int.
    3807             :  *
    3808             :  * Type conversion will be performed if needed.
    3809             :  *
    3810             :  * Exactly GetTotalElementsCount() strings must be provided
    3811             :  *
    3812             :  * This is the same as the C function GDALAttributeWriteIntArray()
    3813             :  *
    3814             :  * @param vals Array of int.
    3815             :  * @param nVals Should be equal to GetTotalElementsCount().
    3816             :  * @return true in case of success.
    3817             :  */
    3818          12 : bool GDALAttribute::Write(const int *vals, size_t nVals)
    3819             : {
    3820          12 :     if (nVals != GetTotalElementsCount())
    3821             :     {
    3822           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3823           1 :         return false;
    3824             :     }
    3825          11 :     const auto nDims = GetDimensionCount();
    3826          22 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3827          11 :     std::vector<size_t> count(1 + nDims);
    3828          11 :     const auto &dims = GetDimensions();
    3829          22 :     for (size_t i = 0; i < nDims; i++)
    3830          11 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3831          11 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3832          11 :                  GDALExtendedDataType::Create(GDT_Int32), vals, vals,
    3833          22 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
    3834             : }
    3835             : 
    3836             : /************************************************************************/
    3837             : /*                               Write()                                */
    3838             : /************************************************************************/
    3839             : 
    3840             : /** Write an attribute from an array of int64_t.
    3841             :  *
    3842             :  * Type conversion will be performed if needed.
    3843             :  *
    3844             :  * Exactly GetTotalElementsCount() strings must be provided
    3845             :  *
    3846             :  * This is the same as the C function GDALAttributeWriteLongArray()
    3847             :  *
    3848             :  * @param vals Array of int64_t.
    3849             :  * @param nVals Should be equal to GetTotalElementsCount().
    3850             :  * @return true in case of success.
    3851             :  */
    3852          13 : bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
    3853             : {
    3854          13 :     if (nVals != GetTotalElementsCount())
    3855             :     {
    3856           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3857           0 :         return false;
    3858             :     }
    3859          13 :     const auto nDims = GetDimensionCount();
    3860          26 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3861          13 :     std::vector<size_t> count(1 + nDims);
    3862          13 :     const auto &dims = GetDimensions();
    3863          26 :     for (size_t i = 0; i < nDims; i++)
    3864          13 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3865          13 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3866          13 :                  GDALExtendedDataType::Create(GDT_Int64), vals, vals,
    3867          13 :                  static_cast<size_t>(GetTotalElementsCount()) *
    3868          13 :                      sizeof(int64_t));
    3869             : }
    3870             : 
    3871             : /************************************************************************/
    3872             : /*                               Write()                                */
    3873             : /************************************************************************/
    3874             : 
    3875             : /** Write an attribute from an array of double.
    3876             :  *
    3877             :  * Type conversion will be performed if needed.
    3878             :  *
    3879             :  * Exactly GetTotalElementsCount() strings must be provided
    3880             :  *
    3881             :  * This is the same as the C function GDALAttributeWriteDoubleArray()
    3882             :  *
    3883             :  * @param vals Array of double.
    3884             :  * @param nVals Should be equal to GetTotalElementsCount().
    3885             :  * @return true in case of success.
    3886             :  */
    3887           8 : bool GDALAttribute::Write(const double *vals, size_t nVals)
    3888             : {
    3889           8 :     if (nVals != GetTotalElementsCount())
    3890             :     {
    3891           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3892           1 :         return false;
    3893             :     }
    3894           7 :     const auto nDims = GetDimensionCount();
    3895          14 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3896           7 :     std::vector<size_t> count(1 + nDims);
    3897           7 :     const auto &dims = GetDimensions();
    3898          15 :     for (size_t i = 0; i < nDims; i++)
    3899           8 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3900           7 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3901           7 :                  GDALExtendedDataType::Create(GDT_Float64), vals, vals,
    3902          14 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
    3903             : }
    3904             : 
    3905             : /************************************************************************/
    3906             : /*                            GDALMDArray()                             */
    3907             : /************************************************************************/
    3908             : 
    3909             : //! @cond Doxygen_Suppress
    3910       10069 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
    3911             :                          CPL_UNUSED const std::string &osName,
    3912           0 :                          const std::string &osContext)
    3913             :     :
    3914             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
    3915             :       GDALAbstractMDArray(osParentName, osName),
    3916             : #endif
    3917       10069 :       m_osContext(osContext)
    3918             : {
    3919       10069 : }
    3920             : 
    3921             : //! @endcond
    3922             : 
    3923             : /************************************************************************/
    3924             : /*                          GetTotalCopyCost()                          */
    3925             : /************************************************************************/
    3926             : 
    3927             : /** Return a total "cost" to copy the array.
    3928             :  *
    3929             :  * Used as a parameter for CopyFrom()
    3930             :  */
    3931          73 : GUInt64 GDALMDArray::GetTotalCopyCost() const
    3932             : {
    3933         146 :     return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
    3934         146 :            GetTotalElementsCount() * GetDataType().GetSize();
    3935             : }
    3936             : 
    3937             : /************************************************************************/
    3938             : /*                      CopyFromAllExceptValues()                       */
    3939             : /************************************************************************/
    3940             : 
    3941             : //! @cond Doxygen_Suppress
    3942             : 
    3943         214 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
    3944             :                                           bool bStrict, GUInt64 &nCurCost,
    3945             :                                           const GUInt64 nTotalCost,
    3946             :                                           GDALProgressFunc pfnProgress,
    3947             :                                           void *pProgressData)
    3948             : {
    3949             :     // Nodata setting must be one of the first things done for TileDB
    3950         214 :     const void *pNoData = poSrcArray->GetRawNoDataValue();
    3951         214 :     if (pNoData && poSrcArray->GetDataType() == GetDataType())
    3952             :     {
    3953          13 :         SetRawNoDataValue(pNoData);
    3954             :     }
    3955             : 
    3956         214 :     const bool bThisIsUnscaledArray =
    3957         214 :         dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
    3958         428 :     auto attrs = poSrcArray->GetAttributes();
    3959         297 :     for (const auto &attr : attrs)
    3960             :     {
    3961          83 :         const auto &osAttrName = attr->GetName();
    3962          83 :         if (bThisIsUnscaledArray)
    3963             :         {
    3964           6 :             if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
    3965           7 :                 osAttrName == "valid_min" || osAttrName == "valid_max" ||
    3966           1 :                 osAttrName == "valid_range")
    3967             :             {
    3968           1 :                 continue;
    3969             :             }
    3970             :         }
    3971             : 
    3972          82 :         auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
    3973         164 :                                        attr->GetDataType());
    3974          82 :         if (!dstAttr)
    3975             :         {
    3976           0 :             if (bStrict)
    3977           0 :                 return false;
    3978           0 :             continue;
    3979             :         }
    3980          82 :         auto raw = attr->ReadAsRaw();
    3981          82 :         if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
    3982           0 :             return false;
    3983             :     }
    3984         214 :     if (!attrs.empty())
    3985             :     {
    3986          47 :         nCurCost += attrs.size() * GDALAttribute::COPY_COST;
    3987          81 :         if (pfnProgress &&
    3988          34 :             !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    3989           0 :             return false;
    3990             :     }
    3991             : 
    3992         214 :     auto srcSRS = poSrcArray->GetSpatialRef();
    3993         214 :     if (srcSRS)
    3994             :     {
    3995          22 :         SetSpatialRef(srcSRS.get());
    3996             :     }
    3997             : 
    3998         214 :     const std::string &osUnit(poSrcArray->GetUnit());
    3999         214 :     if (!osUnit.empty())
    4000             :     {
    4001          24 :         SetUnit(osUnit);
    4002             :     }
    4003             : 
    4004         214 :     bool bGotValue = false;
    4005         214 :     GDALDataType eOffsetStorageType = GDT_Unknown;
    4006             :     const double dfOffset =
    4007         214 :         poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
    4008         214 :     if (bGotValue)
    4009             :     {
    4010           3 :         SetOffset(dfOffset, eOffsetStorageType);
    4011             :     }
    4012             : 
    4013         214 :     bGotValue = false;
    4014         214 :     GDALDataType eScaleStorageType = GDT_Unknown;
    4015         214 :     const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
    4016         214 :     if (bGotValue)
    4017             :     {
    4018           3 :         SetScale(dfScale, eScaleStorageType);
    4019             :     }
    4020             : 
    4021         214 :     return true;
    4022             : }
    4023             : 
    4024             : //! @endcond
    4025             : 
    4026             : /************************************************************************/
    4027             : /*                              CopyFrom()                              */
    4028             : /************************************************************************/
    4029             : 
    4030             : /** Copy the content of an array into a new (generally empty) array.
    4031             :  *
    4032             :  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
    4033             :  *                   of some output drivers this is not recommended)
    4034             :  * @param poSrcArray Source array. Should NOT be nullptr.
    4035             :  * @param bStrict Whether to enable strict mode. In strict mode, any error will
    4036             :  *                stop the copy. In relaxed mode, the copy will be attempted to
    4037             :  *                be pursued.
    4038             :  * @param nCurCost  Should be provided as a variable initially set to 0.
    4039             :  * @param nTotalCost Total cost from GetTotalCopyCost().
    4040             :  * @param pfnProgress Progress callback, or nullptr.
    4041             :  * @param pProgressData Progress user data, or nullptr.
    4042             :  *
    4043             :  * @return true in case of success (or partial success if bStrict == false).
    4044             :  */
    4045          68 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
    4046             :                            const GDALMDArray *poSrcArray, bool bStrict,
    4047             :                            GUInt64 &nCurCost, const GUInt64 nTotalCost,
    4048             :                            GDALProgressFunc pfnProgress, void *pProgressData)
    4049             : {
    4050          68 :     if (pfnProgress == nullptr)
    4051           4 :         pfnProgress = GDALDummyProgress;
    4052             : 
    4053          68 :     nCurCost += GDALMDArray::COPY_COST;
    4054             : 
    4055          68 :     if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
    4056             :                                  pfnProgress, pProgressData))
    4057             :     {
    4058           0 :         return false;
    4059             :     }
    4060             : 
    4061          68 :     const auto &dims = poSrcArray->GetDimensions();
    4062          68 :     const auto nDTSize = poSrcArray->GetDataType().GetSize();
    4063          68 :     if (dims.empty())
    4064             :     {
    4065           2 :         std::vector<GByte> abyTmp(nDTSize);
    4066           2 :         if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
    4067           2 :                                GetDataType(), &abyTmp[0]) &&
    4068           2 :               Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
    4069           4 :                     &abyTmp[0])) &&
    4070             :             bStrict)
    4071             :         {
    4072           0 :             return false;
    4073             :         }
    4074           2 :         nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
    4075           2 :         if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    4076           0 :             return false;
    4077             :     }
    4078             :     else
    4079             :     {
    4080          66 :         std::vector<GUInt64> arrayStartIdx(dims.size());
    4081          66 :         std::vector<GUInt64> count(dims.size());
    4082         172 :         for (size_t i = 0; i < dims.size(); i++)
    4083             :         {
    4084         106 :             count[i] = static_cast<size_t>(dims[i]->GetSize());
    4085             :         }
    4086             : 
    4087             :         struct CopyFunc
    4088             :         {
    4089             :             GDALMDArray *poDstArray = nullptr;
    4090             :             std::vector<GByte> abyTmp{};
    4091             :             GDALProgressFunc pfnProgress = nullptr;
    4092             :             void *pProgressData = nullptr;
    4093             :             GUInt64 nCurCost = 0;
    4094             :             GUInt64 nTotalCost = 0;
    4095             :             GUInt64 nTotalBytesThisArray = 0;
    4096             :             bool bStop = false;
    4097             : 
    4098          84 :             static bool f(GDALAbstractMDArray *l_poSrcArray,
    4099             :                           const GUInt64 *chunkArrayStartIdx,
    4100             :                           const size_t *chunkCount, GUInt64 iCurChunk,
    4101             :                           GUInt64 nChunkCount, void *pUserData)
    4102             :             {
    4103          84 :                 const auto &dt(l_poSrcArray->GetDataType());
    4104          84 :                 auto data = static_cast<CopyFunc *>(pUserData);
    4105          84 :                 auto poDstArray = data->poDstArray;
    4106          84 :                 if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
    4107          84 :                                         nullptr, dt, &data->abyTmp[0]))
    4108             :                 {
    4109           1 :                     return false;
    4110             :                 }
    4111             :                 bool bRet =
    4112          83 :                     poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
    4113          83 :                                       nullptr, dt, &data->abyTmp[0]);
    4114          83 :                 if (dt.NeedsFreeDynamicMemory())
    4115             :                 {
    4116           5 :                     const auto l_nDTSize = dt.GetSize();
    4117           5 :                     GByte *ptr = &data->abyTmp[0];
    4118           5 :                     const size_t l_nDims(l_poSrcArray->GetDimensionCount());
    4119           5 :                     size_t nEltCount = 1;
    4120          10 :                     for (size_t i = 0; i < l_nDims; ++i)
    4121             :                     {
    4122           5 :                         nEltCount *= chunkCount[i];
    4123             :                     }
    4124          22 :                     for (size_t i = 0; i < nEltCount; i++)
    4125             :                     {
    4126          17 :                         dt.FreeDynamicMemory(ptr);
    4127          17 :                         ptr += l_nDTSize;
    4128             :                     }
    4129             :                 }
    4130          83 :                 if (!bRet)
    4131             :                 {
    4132           0 :                     return false;
    4133             :                 }
    4134             : 
    4135          83 :                 double dfCurCost =
    4136          83 :                     double(data->nCurCost) + double(iCurChunk) / nChunkCount *
    4137          83 :                                                  data->nTotalBytesThisArray;
    4138          83 :                 if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
    4139             :                                        data->pProgressData))
    4140             :                 {
    4141           0 :                     data->bStop = true;
    4142           0 :                     return false;
    4143             :                 }
    4144             : 
    4145          83 :                 return true;
    4146             :             }
    4147             :         };
    4148             : 
    4149          66 :         CopyFunc copyFunc;
    4150          66 :         copyFunc.poDstArray = this;
    4151          66 :         copyFunc.nCurCost = nCurCost;
    4152          66 :         copyFunc.nTotalCost = nTotalCost;
    4153          66 :         copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
    4154          66 :         copyFunc.pfnProgress = pfnProgress;
    4155          66 :         copyFunc.pProgressData = pProgressData;
    4156             :         const char *pszSwathSize =
    4157          66 :             CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
    4158             :         const size_t nMaxChunkSize =
    4159             :             pszSwathSize
    4160          66 :                 ? static_cast<size_t>(
    4161           1 :                       std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    4162           1 :                                CPLAtoGIntBig(pszSwathSize)))
    4163             :                 : static_cast<size_t>(
    4164          65 :                       std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    4165          65 :                                GDALGetCacheMax64() / 4));
    4166          66 :         const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
    4167          66 :         size_t nRealChunkSize = nDTSize;
    4168         172 :         for (const auto &nChunkSize : anChunkSizes)
    4169             :         {
    4170         106 :             nRealChunkSize *= nChunkSize;
    4171             :         }
    4172             :         try
    4173             :         {
    4174          66 :             copyFunc.abyTmp.resize(nRealChunkSize);
    4175             :         }
    4176           0 :         catch (const std::exception &)
    4177             :         {
    4178           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    4179             :                      "Cannot allocate temporary buffer");
    4180           0 :             nCurCost += copyFunc.nTotalBytesThisArray;
    4181           0 :             return false;
    4182             :         }
    4183         197 :         if (copyFunc.nTotalBytesThisArray != 0 &&
    4184          65 :             !const_cast<GDALMDArray *>(poSrcArray)
    4185          65 :                  ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
    4186             :                                    anChunkSizes.data(), CopyFunc::f,
    4187         132 :                                    &copyFunc) &&
    4188           1 :             (bStrict || copyFunc.bStop))
    4189             :         {
    4190           0 :             nCurCost += copyFunc.nTotalBytesThisArray;
    4191           0 :             return false;
    4192             :         }
    4193          66 :         nCurCost += copyFunc.nTotalBytesThisArray;
    4194             :     }
    4195             : 
    4196          68 :     return true;
    4197             : }
    4198             : 
    4199             : /************************************************************************/
    4200             : /*                         GetStructuralInfo()                          */
    4201             : /************************************************************************/
    4202             : 
    4203             : /** Return structural information on the array.
    4204             :  *
    4205             :  * This may be the compression, etc..
    4206             :  *
    4207             :  * The return value should not be freed and is valid until GDALMDArray is
    4208             :  * released or this function called again.
    4209             :  *
    4210             :  * This is the same as the C function GDALMDArrayGetStructuralInfo().
    4211             :  */
    4212          95 : CSLConstList GDALMDArray::GetStructuralInfo() const
    4213             : {
    4214          95 :     return nullptr;
    4215             : }
    4216             : 
    4217             : /************************************************************************/
    4218             : /*                             AdviseRead()                             */
    4219             : /************************************************************************/
    4220             : 
    4221             : /** Advise driver of upcoming read requests.
    4222             :  *
    4223             :  * Some GDAL drivers operate more efficiently if they know in advance what
    4224             :  * set of upcoming read requests will be made.  The AdviseRead() method allows
    4225             :  * an application to notify the driver of the region of interest.
    4226             :  *
    4227             :  * Many drivers just ignore the AdviseRead() call, but it can dramatically
    4228             :  * accelerate access via some drivers. One such case is when reading through
    4229             :  * a DAP dataset with the netCDF driver (a in-memory cache array is then created
    4230             :  * with the region of interest defined by AdviseRead())
    4231             :  *
    4232             :  * This is the same as the C function GDALMDArrayAdviseRead().
    4233             :  *
    4234             :  * @param arrayStartIdx Values representing the starting index to read
    4235             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    4236             :  *                      Array of GetDimensionCount() values.
    4237             :  *                      Can be nullptr as a synonymous for [0 for i in
    4238             :  * range(GetDimensionCount() ]
    4239             :  *
    4240             :  * @param count         Values representing the number of values to extract in
    4241             :  *                      each dimension.
    4242             :  *                      Array of GetDimensionCount() values.
    4243             :  *                      Can be nullptr as a synonymous for
    4244             :  *                      [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
    4245             :  * range(GetDimensionCount() ]
    4246             :  *
    4247             :  * @param papszOptions Driver specific options, or nullptr. Consult driver
    4248             :  * documentation.
    4249             :  *
    4250             :  * @return true in case of success (ignoring the advice is a success)
    4251             :  *
    4252             :  * @since GDAL 3.2
    4253             :  */
    4254          68 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    4255             :                              CSLConstList papszOptions) const
    4256             : {
    4257          68 :     const auto nDimCount = GetDimensionCount();
    4258          68 :     if (nDimCount == 0)
    4259           2 :         return true;
    4260             : 
    4261         132 :     std::vector<GUInt64> tmp_arrayStartIdx;
    4262          66 :     if (arrayStartIdx == nullptr)
    4263             :     {
    4264           0 :         tmp_arrayStartIdx.resize(nDimCount);
    4265           0 :         arrayStartIdx = tmp_arrayStartIdx.data();
    4266             :     }
    4267             : 
    4268         132 :     std::vector<size_t> tmp_count;
    4269          66 :     if (count == nullptr)
    4270             :     {
    4271           0 :         tmp_count.resize(nDimCount);
    4272           0 :         const auto &dims = GetDimensions();
    4273           0 :         for (size_t i = 0; i < nDimCount; i++)
    4274             :         {
    4275           0 :             const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
    4276             : #if SIZEOF_VOIDP < 8
    4277             :             if (nSize != static_cast<size_t>(nSize))
    4278             :             {
    4279             :                 CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
    4280             :                 return false;
    4281             :             }
    4282             : #endif
    4283           0 :             tmp_count[i] = static_cast<size_t>(nSize);
    4284             :         }
    4285           0 :         count = tmp_count.data();
    4286             :     }
    4287             : 
    4288         132 :     std::vector<GInt64> tmp_arrayStep;
    4289         132 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    4290          66 :     const GInt64 *arrayStep = nullptr;
    4291          66 :     const GPtrDiff_t *bufferStride = nullptr;
    4292          66 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    4293         132 :                               GDALExtendedDataType::Create(GDT_Unknown),
    4294             :                               nullptr, nullptr, 0, tmp_arrayStep,
    4295             :                               tmp_bufferStride))
    4296             :     {
    4297           2 :         return false;
    4298             :     }
    4299             : 
    4300          64 :     return IAdviseRead(arrayStartIdx, count, papszOptions);
    4301             : }
    4302             : 
    4303             : /************************************************************************/
    4304             : /*                            IAdviseRead()                             */
    4305             : /************************************************************************/
    4306             : 
    4307             : //! @cond Doxygen_Suppress
    4308          19 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
    4309             :                               CSLConstList /* papszOptions*/) const
    4310             : {
    4311          19 :     return true;
    4312             : }
    4313             : 
    4314             : //! @endcond
    4315             : 
    4316             : /************************************************************************/
    4317             : /*                            MassageName()                             */
    4318             : /************************************************************************/
    4319             : 
    4320             : //! @cond Doxygen_Suppress
    4321          49 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
    4322             : {
    4323          49 :     std::string ret;
    4324         717 :     for (const char ch : inputName)
    4325             :     {
    4326         668 :         if (!isalnum(static_cast<unsigned char>(ch)))
    4327         156 :             ret += '_';
    4328             :         else
    4329         512 :             ret += ch;
    4330             :     }
    4331          49 :     return ret;
    4332             : }
    4333             : 
    4334             : //! @endcond
    4335             : 
    4336             : /************************************************************************/
    4337             : /*                         GetCacheRootGroup()                          */
    4338             : /************************************************************************/
    4339             : 
    4340             : //! @cond Doxygen_Suppress
    4341             : std::shared_ptr<GDALGroup>
    4342        3554 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
    4343             :                                std::string &osCacheFilenameOut) const
    4344             : {
    4345        3554 :     const auto &osFilename = GetFilename();
    4346        3554 :     if (osFilename.empty())
    4347             :     {
    4348           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4349             :                  "Cannot cache an array with an empty filename");
    4350           1 :         return nullptr;
    4351             :     }
    4352             : 
    4353        3553 :     osCacheFilenameOut = osFilename + ".gmac";
    4354        3553 :     if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
    4355             :     {
    4356           2 :         const auto nPosQuestionMark = osFilename.find('?');
    4357           2 :         if (nPosQuestionMark != std::string::npos)
    4358             :         {
    4359             :             osCacheFilenameOut =
    4360           0 :                 osFilename.substr(0, nPosQuestionMark)
    4361           0 :                     .append(".gmac")
    4362           0 :                     .append(osFilename.substr(nPosQuestionMark));
    4363             :         }
    4364             :     }
    4365        3553 :     const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
    4366        3553 :     if (pszProxy != nullptr)
    4367           7 :         osCacheFilenameOut = pszProxy;
    4368             : 
    4369             :     // .gmac sidecars are local-only; skip stat for non-local filesystems.
    4370        7086 :     if (!bCanCreate && pszProxy == nullptr &&
    4371        3533 :         !VSIIsLocal(osCacheFilenameOut.c_str()))
    4372             :     {
    4373           2 :         return nullptr;
    4374             :     }
    4375             : 
    4376        3551 :     std::unique_ptr<GDALDataset> poDS;
    4377             :     VSIStatBufL sStat;
    4378        3551 :     if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
    4379             :     {
    4380          42 :         poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
    4381             :                                      GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
    4382             :                                      nullptr, nullptr, nullptr));
    4383             :     }
    4384        3551 :     if (poDS)
    4385             :     {
    4386          42 :         CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
    4387          42 :         return poDS->GetRootGroup();
    4388             :     }
    4389             : 
    4390        3509 :     if (bCanCreate)
    4391             :     {
    4392           7 :         const char *pszDrvName = "netCDF";
    4393           7 :         GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
    4394           7 :         if (poDrv == nullptr)
    4395             :         {
    4396           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
    4397             :                      pszDrvName);
    4398           0 :             return nullptr;
    4399             :         }
    4400             :         {
    4401          14 :             CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
    4402          14 :             CPLErrorStateBackuper oErrorStateBackuper;
    4403           7 :             poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
    4404             :                                                      nullptr, nullptr));
    4405             :         }
    4406           7 :         if (!poDS)
    4407             :         {
    4408           1 :             pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
    4409           1 :             if (pszProxy)
    4410             :             {
    4411           1 :                 osCacheFilenameOut = pszProxy;
    4412           1 :                 poDS.reset(poDrv->CreateMultiDimensional(
    4413             :                     osCacheFilenameOut.c_str(), nullptr, nullptr));
    4414             :             }
    4415             :         }
    4416           7 :         if (poDS)
    4417             :         {
    4418           7 :             CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
    4419           7 :             return poDS->GetRootGroup();
    4420             :         }
    4421             :         else
    4422             :         {
    4423           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4424             :                      "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
    4425             :                      "configuration option to write the cache in "
    4426             :                      "another directory",
    4427             :                      osCacheFilenameOut.c_str());
    4428             :         }
    4429             :     }
    4430             : 
    4431        3502 :     return nullptr;
    4432             : }
    4433             : 
    4434             : //! @endcond
    4435             : 
    4436             : /************************************************************************/
    4437             : /*                               Cache()                                */
    4438             : /************************************************************************/
    4439             : 
    4440             : /** Cache the content of the array into an auxiliary filename.
    4441             :  *
    4442             :  * The main purpose of this method is to be able to cache views that are
    4443             :  * expensive to compute, such as transposed arrays.
    4444             :  *
    4445             :  * The array will be stored in a file whose name is the one of
    4446             :  * GetFilename(), with an extra .gmac extension (stands for GDAL
    4447             :  * Multidimensional Array Cache). The cache is a netCDF dataset.
    4448             :  *
    4449             :  * If the .gmac file cannot be written next to the dataset, the
    4450             :  * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
    4451             :  * directory.
    4452             :  *
    4453             :  * The GDALMDArray::Read() method will automatically use the cache when it
    4454             :  * exists. There is no timestamp checks between the source array and the cached
    4455             :  * array. If the source arrays changes, the cache must be manually deleted.
    4456             :  *
    4457             :  * This is the same as the C function GDALMDArrayCache()
    4458             :  *
    4459             :  * @note Driver implementation: optionally implemented.
    4460             :  *
    4461             :  * @param papszOptions List of options, null terminated, or NULL. Currently
    4462             :  *                     the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
    4463             :  *                     to specify the block size of the cached array.
    4464             :  * @return true in case of success.
    4465             :  */
    4466           7 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
    4467             : {
    4468          14 :     std::string osCacheFilename;
    4469          14 :     auto poRG = GetCacheRootGroup(true, osCacheFilename);
    4470           7 :     if (!poRG)
    4471           1 :         return false;
    4472             : 
    4473          12 :     const std::string osCachedArrayName(MassageName(GetFullName()));
    4474           6 :     if (poRG->OpenMDArray(osCachedArrayName))
    4475             :     {
    4476           2 :         CPLError(CE_Failure, CPLE_NotSupported,
    4477             :                  "An array with same name %s already exists in %s",
    4478             :                  osCachedArrayName.c_str(), osCacheFilename.c_str());
    4479           2 :         return false;
    4480             :     }
    4481             : 
    4482           8 :     CPLStringList aosOptions;
    4483           4 :     aosOptions.SetNameValue("COMPRESS", "DEFLATE");
    4484           4 :     const auto &aoDims = GetDimensions();
    4485           8 :     std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
    4486           4 :     if (!aoDims.empty())
    4487             :     {
    4488             :         std::string osBlockSize(
    4489           4 :             CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
    4490           4 :         if (osBlockSize.empty())
    4491             :         {
    4492           6 :             const auto anBlockSize = GetBlockSize();
    4493           3 :             int idxDim = 0;
    4494          10 :             for (auto nBlockSize : anBlockSize)
    4495             :             {
    4496           7 :                 if (idxDim > 0)
    4497           4 :                     osBlockSize += ',';
    4498           7 :                 if (nBlockSize == 0)
    4499           7 :                     nBlockSize = 256;
    4500           7 :                 nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
    4501             :                 osBlockSize +=
    4502           7 :                     std::to_string(static_cast<uint64_t>(nBlockSize));
    4503           7 :                 idxDim++;
    4504             :             }
    4505             :         }
    4506           4 :         aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
    4507             : 
    4508           4 :         int idxDim = 0;
    4509          13 :         for (const auto &poDim : aoDims)
    4510             :         {
    4511           9 :             auto poNewDim = poRG->CreateDimension(
    4512          18 :                 osCachedArrayName + '_' + std::to_string(idxDim),
    4513          18 :                 poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
    4514           9 :             if (!poNewDim)
    4515           0 :                 return false;
    4516           9 :             aoNewDims.emplace_back(poNewDim);
    4517           9 :             idxDim++;
    4518             :         }
    4519             :     }
    4520             : 
    4521           4 :     auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
    4522           8 :                                              GetDataType(), aosOptions.List());
    4523           4 :     if (!poCachedArray)
    4524             :     {
    4525           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
    4526             :                  osCachedArrayName.c_str(), osCacheFilename.c_str());
    4527           0 :         return false;
    4528             :     }
    4529             : 
    4530           4 :     GUInt64 nCost = 0;
    4531           8 :     return poCachedArray->CopyFrom(nullptr, this,
    4532             :                                    false,  // strict
    4533           4 :                                    nCost, GetTotalCopyCost(), nullptr, nullptr);
    4534             : }
    4535             : 
    4536             : /************************************************************************/
    4537             : /*                                Read()                                */
    4538             : /************************************************************************/
    4539             : 
    4540        6438 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
    4541             :                        const GInt64 *arrayStep,         // step in elements
    4542             :                        const GPtrDiff_t *bufferStride,  // stride in elements
    4543             :                        const GDALExtendedDataType &bufferDataType,
    4544             :                        void *pDstBuffer, const void *pDstBufferAllocStart,
    4545             :                        size_t nDstBufferAllocSize) const
    4546             : {
    4547        6438 :     if (!m_bHasTriedCachedArray)
    4548             :     {
    4549        3469 :         m_bHasTriedCachedArray = true;
    4550        3469 :         if (IsCacheable())
    4551             :         {
    4552        3469 :             const auto &osFilename = GetFilename();
    4553        6216 :             if (!osFilename.empty() &&
    4554        6216 :                 !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
    4555             :             {
    4556        5458 :                 std::string osCacheFilename;
    4557        5458 :                 auto poRG = GetCacheRootGroup(false, osCacheFilename);
    4558        2729 :                 if (poRG)
    4559             :                 {
    4560             :                     const std::string osCachedArrayName(
    4561          44 :                         MassageName(GetFullName()));
    4562          22 :                     m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
    4563          22 :                     if (m_poCachedArray)
    4564             :                     {
    4565           6 :                         const auto &dims = GetDimensions();
    4566             :                         const auto &cachedDims =
    4567           6 :                             m_poCachedArray->GetDimensions();
    4568           6 :                         const size_t nDims = dims.size();
    4569             :                         bool ok =
    4570          12 :                             m_poCachedArray->GetDataType() == GetDataType() &&
    4571           6 :                             cachedDims.size() == nDims;
    4572          19 :                         for (size_t i = 0; ok && i < nDims; ++i)
    4573             :                         {
    4574          13 :                             ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
    4575             :                         }
    4576           6 :                         if (ok)
    4577             :                         {
    4578           6 :                             CPLDebug("GDAL", "Cached array for %s found in %s",
    4579             :                                      osCachedArrayName.c_str(),
    4580             :                                      osCacheFilename.c_str());
    4581             :                         }
    4582             :                         else
    4583             :                         {
    4584           0 :                             CPLError(CE_Warning, CPLE_AppDefined,
    4585             :                                      "Cached array %s in %s has incompatible "
    4586             :                                      "characteristics with current array.",
    4587             :                                      osCachedArrayName.c_str(),
    4588             :                                      osCacheFilename.c_str());
    4589           0 :                             m_poCachedArray.reset();
    4590             :                         }
    4591             :                     }
    4592             :                 }
    4593             :             }
    4594             :         }
    4595             :     }
    4596             : 
    4597        6438 :     const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
    4598        6438 :     if (!array->GetDataType().CanConvertTo(bufferDataType))
    4599             :     {
    4600           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4601             :                  "Array data type is not convertible to buffer data type");
    4602           0 :         return false;
    4603             :     }
    4604             : 
    4605       12876 :     std::vector<GInt64> tmp_arrayStep;
    4606       12876 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    4607        6438 :     if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
    4608             :                                      bufferStride, bufferDataType, pDstBuffer,
    4609             :                                      pDstBufferAllocStart, nDstBufferAllocSize,
    4610             :                                      tmp_arrayStep, tmp_bufferStride))
    4611             :     {
    4612           9 :         return false;
    4613             :     }
    4614             : 
    4615        6429 :     return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
    4616        6429 :                         bufferDataType, pDstBuffer);
    4617             : }
    4618             : 
    4619             : /************************************************************************/
    4620             : /*                            GetRootGroup()                            */
    4621             : /************************************************************************/
    4622             : 
    4623             : /** Return the root group to which this arrays belongs too.
    4624             :  *
    4625             :  * Note that arrays may be free standing and some drivers may not implement
    4626             :  * this method, hence nullptr may be returned.
    4627             :  *
    4628             :  * It is used internally by the GetResampled() method to detect if GLT
    4629             :  * orthorectification is available.
    4630             :  *
    4631             :  * @return the root group, or nullptr.
    4632             :  * @since GDAL 3.8
    4633             :  */
    4634           0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
    4635             : {
    4636           0 :     return nullptr;
    4637             : }
    4638             : 
    4639             : //! @cond Doxygen_Suppress
    4640             : 
    4641             : /************************************************************************/
    4642             : /*                        IsTransposedRequest()                         */
    4643             : /************************************************************************/
    4644             : 
    4645        1541 : bool GDALMDArray::IsTransposedRequest(
    4646             :     const size_t *count,
    4647             :     const GPtrDiff_t *bufferStride) const  // stride in elements
    4648             : {
    4649             :     /*
    4650             :     For example:
    4651             :     count = [2,3,4]
    4652             :     strides = [12, 4, 1]            (2-1)*12+(3-1)*4+(4-1)*1=23   ==> row major
    4653             :     stride [12, 1, 3]            (2-1)*12+(3-1)*1+(4-1)*3=23   ==>
    4654             :     (axis[0],axis[2],axis[1]) transposition [1, 8, 2]             (2-1)*1+
    4655             :     (3-1)*8+(4-1)*2=23   ==> (axis[2],axis[1],axis[0]) transposition
    4656             :     */
    4657        1541 :     const size_t nDims(GetDimensionCount());
    4658        1541 :     size_t nCurStrideForRowMajorStrides = 1;
    4659        1541 :     bool bRowMajorStrides = true;
    4660        1541 :     size_t nElts = 1;
    4661        1541 :     size_t nLastIdx = 0;
    4662        3885 :     for (size_t i = nDims; i > 0;)
    4663             :     {
    4664        2344 :         --i;
    4665        2344 :         if (bufferStride[i] < 0)
    4666           0 :             return false;
    4667        2344 :         if (static_cast<size_t>(bufferStride[i]) !=
    4668             :             nCurStrideForRowMajorStrides)
    4669             :         {
    4670         482 :             bRowMajorStrides = false;
    4671             :         }
    4672             :         // Integer overflows have already been checked in CheckReadWriteParams()
    4673        2344 :         nCurStrideForRowMajorStrides *= count[i];
    4674        2344 :         nElts *= count[i];
    4675        2344 :         nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
    4676             :     }
    4677        1541 :     if (bRowMajorStrides)
    4678        1169 :         return false;
    4679         372 :     return nLastIdx == nElts - 1;
    4680             : }
    4681             : 
    4682             : /************************************************************************/
    4683             : /*                   CopyToFinalBufferSameDataType()                    */
    4684             : /************************************************************************/
    4685             : 
    4686             : template <size_t N>
    4687         104 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
    4688             :                                    size_t nDims, const size_t *count,
    4689             :                                    const GPtrDiff_t *bufferStride)
    4690             : {
    4691         208 :     std::vector<size_t> anStackCount(nDims);
    4692         208 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    4693         104 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
    4694             : #if defined(__GNUC__)
    4695             : #pragma GCC diagnostic push
    4696             : #pragma GCC diagnostic ignored "-Wnull-dereference"
    4697             : #endif
    4698         104 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    4699             : #if defined(__GNUC__)
    4700             : #pragma GCC diagnostic pop
    4701             : #endif
    4702         104 :     size_t iDim = 0;
    4703             : 
    4704         647 : lbl_next_depth:
    4705         647 :     if (iDim == nDims - 1)
    4706             :     {
    4707         531 :         size_t n = count[iDim];
    4708         531 :         GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
    4709         531 :         const auto bufferStrideLastDim = bufferStride[iDim] * N;
    4710       18246 :         while (n > 0)
    4711             :         {
    4712       17715 :             --n;
    4713       17715 :             memcpy(pabyDstBuffer, pabySrcBuffer, N);
    4714       17715 :             pabyDstBuffer += bufferStrideLastDim;
    4715       17715 :             pabySrcBuffer += N;
    4716             :         }
    4717             :     }
    4718             :     else
    4719             :     {
    4720         116 :         anStackCount[iDim] = count[iDim];
    4721             :         while (true)
    4722             :         {
    4723         543 :             ++iDim;
    4724         543 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    4725         543 :             goto lbl_next_depth;
    4726         543 :         lbl_return_to_caller_in_loop:
    4727         543 :             --iDim;
    4728         543 :             --anStackCount[iDim];
    4729         543 :             if (anStackCount[iDim] == 0)
    4730         116 :                 break;
    4731         427 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
    4732             :         }
    4733             :     }
    4734         647 :     if (iDim > 0)
    4735         543 :         goto lbl_return_to_caller_in_loop;
    4736         104 : }
    4737             : 
    4738             : /************************************************************************/
    4739             : /*                         CopyToFinalBuffer()                          */
    4740             : /************************************************************************/
    4741             : 
    4742         352 : static void CopyToFinalBuffer(const void *pSrcBuffer,
    4743             :                               const GDALExtendedDataType &eSrcDataType,
    4744             :                               void *pDstBuffer,
    4745             :                               const GDALExtendedDataType &eDstDataType,
    4746             :                               size_t nDims, const size_t *count,
    4747             :                               const GPtrDiff_t *bufferStride)
    4748             : {
    4749         352 :     const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
    4750             :     // Use specialized implementation for well-known data types when no
    4751             :     // type conversion is needed
    4752         352 :     if (eSrcDataType == eDstDataType)
    4753             :     {
    4754         157 :         if (nSrcDataTypeSize == 1)
    4755             :         {
    4756          77 :             CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
    4757             :                                              count, bufferStride);
    4758         104 :             return;
    4759             :         }
    4760          80 :         else if (nSrcDataTypeSize == 2)
    4761             :         {
    4762           1 :             CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
    4763             :                                              count, bufferStride);
    4764           1 :             return;
    4765             :         }
    4766          79 :         else if (nSrcDataTypeSize == 4)
    4767             :         {
    4768          18 :             CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
    4769             :                                              count, bufferStride);
    4770          18 :             return;
    4771             :         }
    4772          61 :         else if (nSrcDataTypeSize == 8)
    4773             :         {
    4774           8 :             CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
    4775             :                                              count, bufferStride);
    4776           8 :             return;
    4777             :         }
    4778             :     }
    4779             : 
    4780         248 :     const size_t nDstDataTypeSize(eDstDataType.GetSize());
    4781         496 :     std::vector<size_t> anStackCount(nDims);
    4782         496 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    4783         248 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
    4784         248 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    4785         248 :     size_t iDim = 0;
    4786             : 
    4787         517 : lbl_next_depth:
    4788         517 :     if (iDim == nDims - 1)
    4789             :     {
    4790         507 :         GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
    4791         507 :                                          pabyDstBufferStack[iDim], eDstDataType,
    4792         507 :                                          bufferStride[iDim], count[iDim]);
    4793         507 :         pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
    4794             :     }
    4795             :     else
    4796             :     {
    4797          10 :         anStackCount[iDim] = count[iDim];
    4798             :         while (true)
    4799             :         {
    4800         269 :             ++iDim;
    4801         269 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    4802         269 :             goto lbl_next_depth;
    4803         269 :         lbl_return_to_caller_in_loop:
    4804         269 :             --iDim;
    4805         269 :             --anStackCount[iDim];
    4806         269 :             if (anStackCount[iDim] == 0)
    4807          10 :                 break;
    4808         259 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
    4809             :         }
    4810             :     }
    4811         517 :     if (iDim > 0)
    4812         269 :         goto lbl_return_to_caller_in_loop;
    4813             : }
    4814             : 
    4815             : /************************************************************************/
    4816             : /*                         TransposeLast2Dims()                         */
    4817             : /************************************************************************/
    4818             : 
    4819          19 : static bool TransposeLast2Dims(void *pDstBuffer,
    4820             :                                const GDALExtendedDataType &eDT,
    4821             :                                const size_t nDims, const size_t *count,
    4822             :                                const size_t nEltsNonLast2Dims)
    4823             : {
    4824          19 :     const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
    4825          19 :     const auto nDTSize = eDT.GetSize();
    4826             :     void *pTempBufferForLast2DimsTranspose =
    4827          19 :         VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
    4828          19 :     if (pTempBufferForLast2DimsTranspose == nullptr)
    4829           0 :         return false;
    4830             : 
    4831          19 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
    4832          58 :     for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
    4833             :     {
    4834          39 :         GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
    4835             :                         pTempBufferForLast2DimsTranspose,
    4836          39 :                         eDT.GetNumericDataType(), count[nDims - 1],
    4837          39 :                         count[nDims - 2]);
    4838          39 :         memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
    4839             :                nDTSize * nEltsLast2Dims);
    4840          39 :         pabyDstBuffer += nDTSize * nEltsLast2Dims;
    4841             :     }
    4842             : 
    4843          19 :     VSIFree(pTempBufferForLast2DimsTranspose);
    4844             : 
    4845          19 :     return true;
    4846             : }
    4847             : 
    4848             : /************************************************************************/
    4849             : /*                      ReadForTransposedRequest()                      */
    4850             : /************************************************************************/
    4851             : 
    4852             : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
    4853             : // transposed view yield to extremely poor/unusable performance. This fixes
    4854             : // this by using temporary memory to read in a contiguous buffer in a
    4855             : // row-major order, and then do the transposition to the final buffer.
    4856             : 
    4857         371 : bool GDALMDArray::ReadForTransposedRequest(
    4858             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    4859             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
    4860             :     void *pDstBuffer) const
    4861             : {
    4862         371 :     const size_t nDims(GetDimensionCount());
    4863         371 :     if (nDims == 0)
    4864             :     {
    4865           0 :         CPLAssert(false);
    4866             :         return false;  // shouldn't happen
    4867             :     }
    4868         371 :     size_t nElts = 1;
    4869         892 :     for (size_t i = 0; i < nDims; ++i)
    4870         521 :         nElts *= count[i];
    4871             : 
    4872         742 :     std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
    4873         371 :     tmpBufferStrides.back() = 1;
    4874         521 :     for (size_t i = nDims - 1; i > 0;)
    4875             :     {
    4876         150 :         --i;
    4877         150 :         tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
    4878             :     }
    4879             : 
    4880         371 :     const auto &eDT = GetDataType();
    4881         371 :     const auto nDTSize = eDT.GetSize();
    4882         547 :     if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
    4883         563 :         static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
    4884          16 :         (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
    4885             :     {
    4886             :         // Optimization of the optimization if only the last 2 dims are
    4887             :         // transposed that saves on temporary buffer allocation
    4888          23 :         const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
    4889          23 :         size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
    4890          23 :         bool bRowMajorStridesForNonLast2Dims = true;
    4891          23 :         size_t nEltsNonLast2Dims = 1;
    4892          40 :         for (size_t i = nDims - 2; i > 0;)
    4893             :         {
    4894          17 :             --i;
    4895          17 :             if (static_cast<size_t>(bufferStride[i]) !=
    4896             :                 nCurStrideForRowMajorStrides)
    4897             :             {
    4898           4 :                 bRowMajorStridesForNonLast2Dims = false;
    4899             :             }
    4900             :             // Integer overflows have already been checked in
    4901             :             // CheckReadWriteParams()
    4902          17 :             nCurStrideForRowMajorStrides *= count[i];
    4903          17 :             nEltsNonLast2Dims *= count[i];
    4904             :         }
    4905          23 :         if (bRowMajorStridesForNonLast2Dims)
    4906             :         {
    4907             :             // We read in the final buffer!
    4908          19 :             if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
    4909          19 :                        eDT, pDstBuffer))
    4910             :             {
    4911           0 :                 return false;
    4912             :             }
    4913             : 
    4914          19 :             return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
    4915          19 :                                       nEltsNonLast2Dims);
    4916             :         }
    4917             :     }
    4918             : 
    4919         352 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
    4920         352 :     if (pTempBuffer == nullptr)
    4921           0 :         return false;
    4922             : 
    4923         352 :     if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
    4924         352 :                pTempBuffer))
    4925             :     {
    4926           0 :         VSIFree(pTempBuffer);
    4927           0 :         return false;
    4928             :     }
    4929         352 :     CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
    4930             :                       count, bufferStride);
    4931             : 
    4932         352 :     if (eDT.NeedsFreeDynamicMemory())
    4933             :     {
    4934         237 :         GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
    4935         474 :         for (size_t i = 0; i < nElts; ++i)
    4936             :         {
    4937         237 :             eDT.FreeDynamicMemory(pabyPtr);
    4938         237 :             pabyPtr += nDTSize;
    4939             :         }
    4940             :     }
    4941             : 
    4942         352 :     VSIFree(pTempBuffer);
    4943         352 :     return true;
    4944             : }
    4945             : 
    4946             : /************************************************************************/
    4947             : /*           IsStepOneContiguousRowMajorOrderedSameDataType()           */
    4948             : /************************************************************************/
    4949             : 
    4950             : // Returns true if at all following conditions are met:
    4951             : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
    4952             : // defines a row-major ordered contiguous buffer.
    4953          78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
    4954             :     const size_t *count, const GInt64 *arrayStep,
    4955             :     const GPtrDiff_t *bufferStride,
    4956             :     const GDALExtendedDataType &bufferDataType) const
    4957             : {
    4958          78 :     if (bufferDataType != GetDataType())
    4959           5 :         return false;
    4960          73 :     size_t nExpectedStride = 1;
    4961         166 :     for (size_t i = GetDimensionCount(); i > 0;)
    4962             :     {
    4963          96 :         --i;
    4964          96 :         if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
    4965          94 :             static_cast<size_t>(bufferStride[i]) != nExpectedStride)
    4966             :         {
    4967           3 :             return false;
    4968             :         }
    4969          93 :         nExpectedStride *= count[i];
    4970             :     }
    4971          70 :     return true;
    4972             : }
    4973             : 
    4974             : /************************************************************************/
    4975             : /*                      ReadUsingContiguousIRead()                      */
    4976             : /************************************************************************/
    4977             : 
    4978             : // Used for example by the TileDB driver when requesting it with
    4979             : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
    4980             : // not defining a row-major ordered contiguous buffer.
    4981             : // Should only be called when at least one of the above conditions are true,
    4982             : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
    4983             : // returning none.
    4984             : // This method will call IRead() again with arrayStep[] == 1,
    4985             : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
    4986             : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
    4987             : // content of that temporary buffer onto pDstBuffer.
    4988           7 : bool GDALMDArray::ReadUsingContiguousIRead(
    4989             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    4990             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
    4991             :     void *pDstBuffer) const
    4992             : {
    4993           7 :     const size_t nDims(GetDimensionCount());
    4994          14 :     std::vector<GUInt64> anTmpStartIdx(nDims);
    4995          14 :     std::vector<size_t> anTmpCount(nDims);
    4996           7 :     const auto &oType = GetDataType();
    4997           7 :     size_t nMemArraySize = oType.GetSize();
    4998          14 :     std::vector<GPtrDiff_t> anTmpStride(nDims);
    4999           7 :     GPtrDiff_t nStride = 1;
    5000          18 :     for (size_t i = nDims; i > 0;)
    5001             :     {
    5002          11 :         --i;
    5003          11 :         if (arrayStep[i] > 0)
    5004           9 :             anTmpStartIdx[i] = arrayStartIdx[i];
    5005             :         else
    5006           2 :             anTmpStartIdx[i] =
    5007           2 :                 arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
    5008             :         const uint64_t nCount =
    5009          11 :             (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
    5010          11 :         if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
    5011             :         {
    5012           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    5013             :                      "Read() failed due to too large memory requirement");
    5014           0 :             return false;
    5015             :         }
    5016          11 :         anTmpCount[i] = static_cast<size_t>(nCount);
    5017          11 :         nMemArraySize *= anTmpCount[i];
    5018          11 :         anTmpStride[i] = nStride;
    5019          11 :         nStride *= anTmpCount[i];
    5020             :     }
    5021             :     std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
    5022          14 :         VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
    5023           7 :     if (!pTmpBuffer)
    5024           0 :         return false;
    5025           7 :     if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
    5026          14 :                std::vector<GInt64>(nDims, 1).data(),  // steps
    5027           7 :                anTmpStride.data(), oType, pTmpBuffer.get()))
    5028             :     {
    5029           0 :         return false;
    5030             :     }
    5031          14 :     std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
    5032          18 :     for (size_t i = 0; i < nDims; ++i)
    5033             :     {
    5034          11 :         if (arrayStep[i] > 0)
    5035           9 :             anTmpStartIdx[i] = 0;
    5036             :         else
    5037           2 :             anTmpStartIdx[i] = anTmpCount[i] - 1;
    5038          22 :         apoTmpDims[i] = std::make_shared<GDALDimension>(
    5039          22 :             std::string(), std::string(), std::string(), std::string(),
    5040          22 :             anTmpCount[i]);
    5041             :     }
    5042             :     auto poMEMArray =
    5043          14 :         MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
    5044          21 :     return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
    5045           7 :            poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
    5046           7 :                             bufferStride, bufferDataType, pDstBuffer);
    5047             : }
    5048             : 
    5049             : //! @endcond
    5050             : 
    5051             : /************************************************************************/
    5052             : /*                          GDALSlicedMDArray                           */
    5053             : /************************************************************************/
    5054             : 
    5055             : class GDALSlicedMDArray final : public GDALPamMDArray
    5056             : {
    5057             :   private:
    5058             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5059             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    5060             :     std::vector<size_t> m_mapDimIdxToParentDimIdx{};  // of size m_dims.size()
    5061             :     std::vector<std::shared_ptr<GDALMDArray>> m_apoNewIndexingVariables{};
    5062             :     std::vector<Range>
    5063             :         m_parentRanges{};  // of size m_poParent->GetDimensionCount()
    5064             : 
    5065             :     mutable std::vector<GUInt64> m_parentStart;
    5066             :     mutable std::vector<size_t> m_parentCount;
    5067             :     mutable std::vector<GInt64> m_parentStep;
    5068             :     mutable std::vector<GPtrDiff_t> m_parentStride;
    5069             : 
    5070             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
    5071             :                              const GInt64 *arrayStep,
    5072             :                              const GPtrDiff_t *bufferStride) const;
    5073             : 
    5074             :   protected:
    5075         862 :     explicit GDALSlicedMDArray(
    5076             :         const std::shared_ptr<GDALMDArray> &poParent,
    5077             :         const std::string &viewExpr,
    5078             :         std::vector<std::shared_ptr<GDALDimension>> &&dims,
    5079             :         std::vector<size_t> &&mapDimIdxToParentDimIdx,
    5080             :         std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
    5081             :         std::vector<Range> &&parentRanges)
    5082        2586 :         : GDALAbstractMDArray(std::string(), "Sliced view of " +
    5083        2586 :                                                  poParent->GetFullName() +
    5084        1724 :                                                  " (" + viewExpr + ")"),
    5085        1724 :           GDALPamMDArray(std::string(),
    5086        1724 :                          "Sliced view of " + poParent->GetFullName() + " (" +
    5087        1724 :                              viewExpr + ")",
    5088        1724 :                          GDALPamMultiDim::GetPAM(poParent),
    5089             :                          poParent->GetContext()),
    5090        1724 :           m_poParent(std::move(poParent)), m_dims(std::move(dims)),
    5091         862 :           m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
    5092         862 :           m_apoNewIndexingVariables(std::move(apoNewIndexingVariables)),
    5093         862 :           m_parentRanges(std::move(parentRanges)),
    5094         862 :           m_parentStart(m_poParent->GetDimensionCount()),
    5095         862 :           m_parentCount(m_poParent->GetDimensionCount(), 1),
    5096         862 :           m_parentStep(m_poParent->GetDimensionCount()),
    5097        6896 :           m_parentStride(m_poParent->GetDimensionCount())
    5098             :     {
    5099         862 :     }
    5100             : 
    5101             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5102             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5103             :                const GDALExtendedDataType &bufferDataType,
    5104             :                void *pDstBuffer) const override;
    5105             : 
    5106             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    5107             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5108             :                 const GDALExtendedDataType &bufferDataType,
    5109             :                 const void *pSrcBuffer) override;
    5110             : 
    5111             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5112             :                      CSLConstList papszOptions) const override;
    5113             : 
    5114             :   public:
    5115             :     static std::shared_ptr<GDALSlicedMDArray>
    5116         862 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5117             :            const std::string &viewExpr,
    5118             :            std::vector<std::shared_ptr<GDALDimension>> &&dims,
    5119             :            std::vector<size_t> &&mapDimIdxToParentDimIdx,
    5120             :            std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
    5121             :            std::vector<Range> &&parentRanges)
    5122             :     {
    5123         862 :         CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
    5124         862 :         CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
    5125             : 
    5126             :         auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
    5127         862 :             poParent, viewExpr, std::move(dims),
    5128         862 :             std::move(mapDimIdxToParentDimIdx),
    5129         862 :             std::move(apoNewIndexingVariables), std::move(parentRanges))));
    5130         862 :         newAr->SetSelf(newAr);
    5131         862 :         return newAr;
    5132             :     }
    5133             : 
    5134         228 :     bool IsWritable() const override
    5135             :     {
    5136         228 :         return m_poParent->IsWritable();
    5137             :     }
    5138             : 
    5139        1834 :     const std::string &GetFilename() const override
    5140             :     {
    5141        1834 :         return m_poParent->GetFilename();
    5142             :     }
    5143             : 
    5144             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5145        5991 :     GetDimensions() const override
    5146             :     {
    5147        5991 :         return m_dims;
    5148             :     }
    5149             : 
    5150        2445 :     const GDALExtendedDataType &GetDataType() const override
    5151             :     {
    5152        2445 :         return m_poParent->GetDataType();
    5153             :     }
    5154             : 
    5155           4 :     const std::string &GetUnit() const override
    5156             :     {
    5157           4 :         return m_poParent->GetUnit();
    5158             :     }
    5159             : 
    5160             :     // bool SetUnit(const std::string& osUnit) override  { return
    5161             :     // m_poParent->SetUnit(osUnit); }
    5162             : 
    5163           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5164             :     {
    5165           4 :         auto poSrcSRS = m_poParent->GetSpatialRef();
    5166           2 :         if (!poSrcSRS)
    5167           1 :             return nullptr;
    5168           2 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
    5169           2 :         std::vector<int> dstMapping;
    5170           3 :         for (int srcAxis : srcMapping)
    5171             :         {
    5172           2 :             bool bFound = false;
    5173           3 :             for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
    5174             :             {
    5175           3 :                 if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
    5176           3 :                     srcAxis - 1)
    5177             :                 {
    5178           2 :                     dstMapping.push_back(static_cast<int>(i) + 1);
    5179           2 :                     bFound = true;
    5180           2 :                     break;
    5181             :                 }
    5182             :             }
    5183           2 :             if (!bFound)
    5184             :             {
    5185           0 :                 dstMapping.push_back(0);
    5186             :             }
    5187             :         }
    5188           2 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
    5189           1 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
    5190           1 :         return poClone;
    5191             :     }
    5192             : 
    5193         104 :     const void *GetRawNoDataValue() const override
    5194             :     {
    5195         104 :         return m_poParent->GetRawNoDataValue();
    5196             :     }
    5197             : 
    5198             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
    5199             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
    5200             : 
    5201           4 :     double GetOffset(bool *pbHasOffset,
    5202             :                      GDALDataType *peStorageType) const override
    5203             :     {
    5204           4 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5205             :     }
    5206             : 
    5207           4 :     double GetScale(bool *pbHasScale,
    5208             :                     GDALDataType *peStorageType) const override
    5209             :     {
    5210           4 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5211             :     }
    5212             : 
    5213             :     // bool SetOffset(double dfOffset) override { return
    5214             :     // m_poParent->SetOffset(dfOffset); }
    5215             : 
    5216             :     // bool SetScale(double dfScale) override { return
    5217             :     // m_poParent->SetScale(dfScale); }
    5218             : 
    5219         580 :     std::vector<GUInt64> GetBlockSize() const override
    5220             :     {
    5221         580 :         std::vector<GUInt64> ret(GetDimensionCount());
    5222        1160 :         const auto parentBlockSize(m_poParent->GetBlockSize());
    5223        1564 :         for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
    5224             :         {
    5225         984 :             const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
    5226         984 :             if (iOldAxis != static_cast<size_t>(-1))
    5227             :             {
    5228         984 :                 ret[i] = parentBlockSize[iOldAxis];
    5229             :             }
    5230             :         }
    5231        1160 :         return ret;
    5232             :     }
    5233             : 
    5234             :     std::shared_ptr<GDALAttribute>
    5235          14 :     GetAttribute(const std::string &osName) const override
    5236             :     {
    5237          14 :         return m_poParent->GetAttribute(osName);
    5238             :     }
    5239             : 
    5240             :     std::vector<std::shared_ptr<GDALAttribute>>
    5241          37 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    5242             :     {
    5243          37 :         return m_poParent->GetAttributes(papszOptions);
    5244             :     }
    5245             : };
    5246             : 
    5247             : /************************************************************************/
    5248             : /*                        PrepareParentArrays()                         */
    5249             : /************************************************************************/
    5250             : 
    5251         763 : void GDALSlicedMDArray::PrepareParentArrays(
    5252             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    5253             :     const GPtrDiff_t *bufferStride) const
    5254             : {
    5255         763 :     const size_t nParentDimCount = m_parentRanges.size();
    5256        2148 :     for (size_t i = 0; i < nParentDimCount; i++)
    5257             :     {
    5258             :         // For dimensions in parent that have no existence in sliced array
    5259        1385 :         m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
    5260             :     }
    5261             : 
    5262        1919 :     for (size_t i = 0; i < m_dims.size(); i++)
    5263             :     {
    5264        1156 :         const auto iParent = m_mapDimIdxToParentDimIdx[i];
    5265        1156 :         if (iParent != static_cast<size_t>(-1))
    5266             :         {
    5267        1154 :             m_parentStart[iParent] =
    5268        1154 :                 m_parentRanges[iParent].m_nIncr >= 0
    5269        1154 :                     ? m_parentRanges[iParent].m_nStartIdx +
    5270         852 :                           arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
    5271         302 :                     : m_parentRanges[iParent].m_nStartIdx -
    5272         604 :                           arrayStartIdx[i] *
    5273         302 :                               static_cast<GUInt64>(
    5274         302 :                                   -m_parentRanges[iParent].m_nIncr);
    5275        1154 :             m_parentCount[iParent] = count[i];
    5276        1154 :             if (arrayStep)
    5277             :             {
    5278        1151 :                 m_parentStep[iParent] =
    5279        1151 :                     count[i] == 1 ? 1 :
    5280             :                                   // other checks should have ensured this does
    5281             :                         // not overflow
    5282         947 :                         arrayStep[i] * m_parentRanges[iParent].m_nIncr;
    5283             :             }
    5284        1154 :             if (bufferStride)
    5285             :             {
    5286        1151 :                 m_parentStride[iParent] = bufferStride[i];
    5287             :             }
    5288             :         }
    5289             :     }
    5290         763 : }
    5291             : 
    5292             : /************************************************************************/
    5293             : /*                               IRead()                                */
    5294             : /************************************************************************/
    5295             : 
    5296         732 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5297             :                               const GInt64 *arrayStep,
    5298             :                               const GPtrDiff_t *bufferStride,
    5299             :                               const GDALExtendedDataType &bufferDataType,
    5300             :                               void *pDstBuffer) const
    5301             : {
    5302         732 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5303        1464 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
    5304         732 :                             m_parentStep.data(), m_parentStride.data(),
    5305         732 :                             bufferDataType, pDstBuffer);
    5306             : }
    5307             : 
    5308             : /************************************************************************/
    5309             : /*                               IWrite()                               */
    5310             : /************************************************************************/
    5311             : 
    5312          29 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
    5313             :                                const size_t *count, const GInt64 *arrayStep,
    5314             :                                const GPtrDiff_t *bufferStride,
    5315             :                                const GDALExtendedDataType &bufferDataType,
    5316             :                                const void *pSrcBuffer)
    5317             : {
    5318          29 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5319          58 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
    5320          29 :                              m_parentStep.data(), m_parentStride.data(),
    5321          29 :                              bufferDataType, pSrcBuffer);
    5322             : }
    5323             : 
    5324             : /************************************************************************/
    5325             : /*                            IAdviseRead()                             */
    5326             : /************************************************************************/
    5327             : 
    5328           2 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
    5329             :                                     const size_t *count,
    5330             :                                     CSLConstList papszOptions) const
    5331             : {
    5332           2 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
    5333           2 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
    5334           2 :                                   papszOptions);
    5335             : }
    5336             : 
    5337             : /************************************************************************/
    5338             : /*                         CreateSlicedArray()                          */
    5339             : /************************************************************************/
    5340             : 
    5341             : static std::shared_ptr<GDALMDArray>
    5342         724 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
    5343             :                   const std::string &viewExpr, const std::string &activeSlice,
    5344             :                   bool bRenameDimensions,
    5345             :                   std::vector<GDALMDArray::ViewSpec> &viewSpecs)
    5346             : {
    5347         724 :     const auto &srcDims(self->GetDimensions());
    5348         724 :     if (srcDims.empty())
    5349             :     {
    5350           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
    5351           2 :         return nullptr;
    5352             :     }
    5353             : 
    5354        1444 :     CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
    5355         722 :     const auto nTokens = static_cast<size_t>(aosTokens.size());
    5356             : 
    5357        1444 :     std::vector<std::shared_ptr<GDALDimension>> newDims;
    5358        1444 :     std::vector<size_t> mapDimIdxToParentDimIdx;
    5359        1444 :     std::vector<GDALSlicedMDArray::Range> parentRanges;
    5360         722 :     newDims.reserve(nTokens);
    5361         722 :     mapDimIdxToParentDimIdx.reserve(nTokens);
    5362         722 :     parentRanges.reserve(nTokens);
    5363             : 
    5364         722 :     bool bGotEllipsis = false;
    5365         722 :     size_t nCurSrcDim = 0;
    5366        1444 :     std::vector<std::shared_ptr<GDALMDArray>> apoNewIndexingVariables;
    5367        2151 :     for (size_t i = 0; i < nTokens; i++)
    5368             :     {
    5369        1446 :         const char *pszIdxSpec = aosTokens[i];
    5370        1446 :         if (EQUAL(pszIdxSpec, "..."))
    5371             :         {
    5372         129 :             if (bGotEllipsis)
    5373             :             {
    5374           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5375             :                          "Only one single ellipsis is supported");
    5376           2 :                 return nullptr;
    5377             :             }
    5378         127 :             bGotEllipsis = true;
    5379         127 :             const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
    5380         263 :             for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
    5381             :             {
    5382         136 :                 parentRanges.emplace_back(0, 1);
    5383         136 :                 newDims.push_back(srcDims[nCurSrcDim]);
    5384         136 :                 mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5385             :             }
    5386         127 :             continue;
    5387             :         }
    5388        1317 :         else if (EQUAL(pszIdxSpec, "newaxis") ||
    5389        1314 :                  EQUAL(pszIdxSpec, "np.newaxis"))
    5390             :         {
    5391           3 :             newDims.push_back(std::make_shared<GDALDimension>(
    5392           6 :                 std::string(), "newaxis", std::string(), std::string(), 1));
    5393           3 :             mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
    5394           3 :             continue;
    5395             :         }
    5396        1314 :         else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
    5397             :         {
    5398         342 :             if (nCurSrcDim >= srcDims.size())
    5399             :             {
    5400           2 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
    5401             :                          activeSlice.c_str());
    5402           7 :                 return nullptr;
    5403             :             }
    5404             : 
    5405         340 :             auto nVal = CPLAtoGIntBig(pszIdxSpec);
    5406         340 :             GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
    5407         340 :             if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
    5408         336 :                 (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
    5409             :             {
    5410           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5411             :                          "Index " CPL_FRMT_GIB " is out of bounds", nVal);
    5412           5 :                 return nullptr;
    5413             :             }
    5414         335 :             if (nVal < 0)
    5415           0 :                 nVal += nDimSize;
    5416         335 :             parentRanges.emplace_back(nVal, 0);
    5417             :         }
    5418             :         else
    5419             :         {
    5420         972 :             if (nCurSrcDim >= srcDims.size())
    5421             :             {
    5422           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
    5423             :                          activeSlice.c_str());
    5424           8 :                 return nullptr;
    5425             :             }
    5426             : 
    5427             :             CPLStringList aosRangeTokens(
    5428         971 :                 CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
    5429         971 :             int nRangeTokens = aosRangeTokens.size();
    5430         971 :             if (nRangeTokens > 3)
    5431             :             {
    5432           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
    5433             :                          pszIdxSpec);
    5434           1 :                 return nullptr;
    5435             :             }
    5436         970 :             if (nRangeTokens <= 1)
    5437             :             {
    5438           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
    5439             :                          pszIdxSpec);
    5440           1 :                 return nullptr;
    5441             :             }
    5442         969 :             const char *pszStart = aosRangeTokens[0];
    5443         969 :             const char *pszEnd = aosRangeTokens[1];
    5444         969 :             const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
    5445         969 :             GDALSlicedMDArray::Range range;
    5446         969 :             const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
    5447         969 :             range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
    5448        1937 :             if (range.m_nIncr == 0 ||
    5449         968 :                 range.m_nIncr == std::numeric_limits<GInt64>::min())
    5450             :             {
    5451           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
    5452           1 :                 return nullptr;
    5453             :             }
    5454         968 :             auto startIdx(CPLAtoGIntBig(pszStart));
    5455         968 :             if (startIdx < 0)
    5456             :             {
    5457           0 :                 if (nDimSize < static_cast<GUInt64>(-startIdx))
    5458           0 :                     startIdx = 0;
    5459             :                 else
    5460           0 :                     startIdx = nDimSize + startIdx;
    5461             :             }
    5462         968 :             const bool bPosIncr = range.m_nIncr > 0;
    5463         968 :             range.m_nStartIdx = startIdx;
    5464        1936 :             range.m_nStartIdx = EQUAL(pszStart, "")
    5465         968 :                                     ? (bPosIncr ? 0 : nDimSize - 1)
    5466             :                                     : range.m_nStartIdx;
    5467         968 :             if (range.m_nStartIdx >= nDimSize - 1)
    5468         286 :                 range.m_nStartIdx = nDimSize - 1;
    5469         968 :             auto endIdx(CPLAtoGIntBig(pszEnd));
    5470         968 :             if (endIdx < 0)
    5471             :             {
    5472           1 :                 const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
    5473           1 :                 if (nDimSize < positiveEndIdx)
    5474           0 :                     endIdx = 0;
    5475             :                 else
    5476           1 :                     endIdx = nDimSize - positiveEndIdx;
    5477             :             }
    5478         968 :             GUInt64 nEndIdx = endIdx;
    5479         968 :             nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
    5480         968 :             if (pszStart[0] || pszEnd[0])
    5481             :             {
    5482         636 :                 if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
    5483         633 :                     (!bPosIncr && range.m_nStartIdx <= nEndIdx))
    5484             :                 {
    5485           4 :                     CPLError(CE_Failure, CPLE_AppDefined,
    5486             :                              "Output dimension of size 0 is not allowed");
    5487           4 :                     return nullptr;
    5488             :                 }
    5489             :             }
    5490         964 :             int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
    5491         964 :             const auto nAbsIncr = std::abs(range.m_nIncr);
    5492         964 :             const GUInt64 newSize =
    5493         332 :                 (pszStart[0] == 0 && pszEnd[0] == 0 &&
    5494         332 :                  range.m_nStartIdx == nEndIdx)
    5495        1928 :                     ? 1
    5496             :                 : bPosIncr
    5497        1091 :                     ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
    5498         128 :                     : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
    5499         964 :             const auto &poSrcDim = srcDims[nCurSrcDim];
    5500        1544 :             if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
    5501         580 :                 newSize == poSrcDim->GetSize())
    5502             :             {
    5503         211 :                 newDims.push_back(poSrcDim);
    5504             :             }
    5505             :             else
    5506             :             {
    5507        1506 :                 std::string osNewDimName(poSrcDim->GetName());
    5508         753 :                 if (bRenameDimensions)
    5509             :                 {
    5510             :                     osNewDimName =
    5511        1410 :                         "subset_" + poSrcDim->GetName() +
    5512             :                         CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
    5513             :                                    "_" CPL_FRMT_GUIB,
    5514         705 :                                    static_cast<GUIntBig>(range.m_nStartIdx),
    5515         705 :                                    static_cast<GIntBig>(range.m_nIncr),
    5516         705 :                                    static_cast<GUIntBig>(newSize));
    5517             :                 }
    5518             :                 auto poNewDim = std::make_shared<GDALDimensionWeakIndexingVar>(
    5519        1506 :                     std::string(), osNewDimName, poSrcDim->GetType(),
    5520         753 :                     range.m_nIncr > 0 ? poSrcDim->GetDirection()
    5521             :                                       : std::string(),
    5522        1506 :                     newSize);
    5523         753 :                 auto poSrcIndexingVar = poSrcDim->GetIndexingVariable();
    5524         910 :                 if (poSrcIndexingVar &&
    5525         910 :                     poSrcIndexingVar->GetDimensionCount() == 1 &&
    5526         157 :                     poSrcIndexingVar->GetDimensions()[0] == poSrcDim)
    5527             :                 {
    5528             :                     std::vector<std::shared_ptr<GDALDimension>>
    5529         628 :                         indexingVarNewDims{poNewDim};
    5530         314 :                     std::vector<size_t> indexingVarMapDimIdxToParentDimIdx{0};
    5531             :                     std::vector<std::shared_ptr<GDALMDArray>>
    5532         314 :                         indexingVarNewIndexingVar;
    5533             :                     std::vector<GDALSlicedMDArray::Range>
    5534         314 :                         indexingVarParentRanges{range};
    5535             :                     auto poNewIndexingVar = GDALSlicedMDArray::Create(
    5536             :                         poSrcIndexingVar, pszIdxSpec,
    5537         157 :                         std::move(indexingVarNewDims),
    5538         157 :                         std::move(indexingVarMapDimIdxToParentDimIdx),
    5539         157 :                         std::move(indexingVarNewIndexingVar),
    5540         471 :                         std::move(indexingVarParentRanges));
    5541         157 :                     poNewDim->SetIndexingVariable(poNewIndexingVar);
    5542         157 :                     apoNewIndexingVariables.push_back(
    5543         157 :                         std::move(poNewIndexingVar));
    5544             :                 }
    5545         753 :                 newDims.push_back(std::move(poNewDim));
    5546             :             }
    5547         964 :             mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5548         964 :             parentRanges.emplace_back(range);
    5549             :         }
    5550             : 
    5551        1299 :         nCurSrcDim++;
    5552             :     }
    5553         778 :     for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
    5554             :     {
    5555          73 :         parentRanges.emplace_back(0, 1);
    5556          73 :         newDims.push_back(srcDims[nCurSrcDim]);
    5557          73 :         mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5558             :     }
    5559             : 
    5560         705 :     GDALMDArray::ViewSpec viewSpec;
    5561         705 :     viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
    5562         705 :     viewSpec.m_parentRanges = parentRanges;
    5563         705 :     viewSpecs.emplace_back(std::move(viewSpec));
    5564             : 
    5565        1410 :     return GDALSlicedMDArray::Create(
    5566         705 :         self, viewExpr, std::move(newDims), std::move(mapDimIdxToParentDimIdx),
    5567        1410 :         std::move(apoNewIndexingVariables), std::move(parentRanges));
    5568             : }
    5569             : 
    5570             : /************************************************************************/
    5571             : /*                       GDALExtractFieldMDArray                        */
    5572             : /************************************************************************/
    5573             : 
    5574             : class GDALExtractFieldMDArray final : public GDALPamMDArray
    5575             : {
    5576             :   private:
    5577             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5578             :     GDALExtendedDataType m_dt;
    5579             :     std::string m_srcCompName;
    5580             :     mutable std::vector<GByte> m_pabyNoData{};
    5581             : 
    5582             :   protected:
    5583         465 :     GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
    5584             :                             const std::string &fieldName,
    5585             :                             const std::unique_ptr<GDALEDTComponent> &srcComp)
    5586        1860 :         : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
    5587         930 :                                                  " of " +
    5588         465 :                                                  poParent->GetFullName()),
    5589             :           GDALPamMDArray(
    5590         930 :               std::string(),
    5591         930 :               "Extract field " + fieldName + " of " + poParent->GetFullName(),
    5592         930 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
    5593             :           m_poParent(poParent), m_dt(srcComp->GetType()),
    5594        2325 :           m_srcCompName(srcComp->GetName())
    5595             :     {
    5596         465 :         m_pabyNoData.resize(m_dt.GetSize());
    5597         465 :     }
    5598             : 
    5599             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5600             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5601             :                const GDALExtendedDataType &bufferDataType,
    5602             :                void *pDstBuffer) const override;
    5603             : 
    5604           1 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5605             :                      CSLConstList papszOptions) const override
    5606             :     {
    5607           1 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
    5608             :     }
    5609             : 
    5610             :   public:
    5611             :     static std::shared_ptr<GDALExtractFieldMDArray>
    5612         465 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5613             :            const std::string &fieldName,
    5614             :            const std::unique_ptr<GDALEDTComponent> &srcComp)
    5615             :     {
    5616             :         auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
    5617         465 :             new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
    5618         465 :         newAr->SetSelf(newAr);
    5619         465 :         return newAr;
    5620             :     }
    5621             : 
    5622         930 :     ~GDALExtractFieldMDArray() override
    5623         465 :     {
    5624         465 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
    5625         930 :     }
    5626             : 
    5627         200 :     bool IsWritable() const override
    5628             :     {
    5629         200 :         return m_poParent->IsWritable();
    5630             :     }
    5631             : 
    5632        1314 :     const std::string &GetFilename() const override
    5633             :     {
    5634        1314 :         return m_poParent->GetFilename();
    5635             :     }
    5636             : 
    5637             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5638        1384 :     GetDimensions() const override
    5639             :     {
    5640        1384 :         return m_poParent->GetDimensions();
    5641             :     }
    5642             : 
    5643        1137 :     const GDALExtendedDataType &GetDataType() const override
    5644             :     {
    5645        1137 :         return m_dt;
    5646             :     }
    5647             : 
    5648           2 :     const std::string &GetUnit() const override
    5649             :     {
    5650           2 :         return m_poParent->GetUnit();
    5651             :     }
    5652             : 
    5653           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5654             :     {
    5655           2 :         return m_poParent->GetSpatialRef();
    5656             :     }
    5657             : 
    5658          99 :     const void *GetRawNoDataValue() const override
    5659             :     {
    5660          99 :         const void *parentNoData = m_poParent->GetRawNoDataValue();
    5661          99 :         if (parentNoData == nullptr)
    5662           6 :             return nullptr;
    5663             : 
    5664          93 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
    5665          93 :         memset(&m_pabyNoData[0], 0, m_dt.GetSize());
    5666             : 
    5667         186 :         std::vector<std::unique_ptr<GDALEDTComponent>> comps;
    5668         186 :         comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
    5669         186 :             new GDALEDTComponent(m_srcCompName, 0, m_dt)));
    5670          93 :         auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
    5671         279 :                                                 std::move(comps)));
    5672             : 
    5673          93 :         GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
    5674          93 :                                         &m_pabyNoData[0], tmpDT);
    5675             : 
    5676          93 :         return &m_pabyNoData[0];
    5677             :     }
    5678             : 
    5679           2 :     double GetOffset(bool *pbHasOffset,
    5680             :                      GDALDataType *peStorageType) const override
    5681             :     {
    5682           2 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5683             :     }
    5684             : 
    5685           2 :     double GetScale(bool *pbHasScale,
    5686             :                     GDALDataType *peStorageType) const override
    5687             :     {
    5688           2 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5689             :     }
    5690             : 
    5691         201 :     std::vector<GUInt64> GetBlockSize() const override
    5692             :     {
    5693         201 :         return m_poParent->GetBlockSize();
    5694             :     }
    5695             : };
    5696             : 
    5697             : /************************************************************************/
    5698             : /*                               IRead()                                */
    5699             : /************************************************************************/
    5700             : 
    5701         414 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
    5702             :                                     const size_t *count,
    5703             :                                     const GInt64 *arrayStep,
    5704             :                                     const GPtrDiff_t *bufferStride,
    5705             :                                     const GDALExtendedDataType &bufferDataType,
    5706             :                                     void *pDstBuffer) const
    5707             : {
    5708         828 :     std::vector<std::unique_ptr<GDALEDTComponent>> comps;
    5709         828 :     comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
    5710         828 :         new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
    5711             :     auto tmpDT(GDALExtendedDataType::Create(
    5712         828 :         std::string(), bufferDataType.GetSize(), std::move(comps)));
    5713             : 
    5714         414 :     return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
    5715         828 :                             tmpDT, pDstBuffer);
    5716             : }
    5717             : 
    5718             : /************************************************************************/
    5719             : /*                    CreateFieldNameExtractArray()                     */
    5720             : /************************************************************************/
    5721             : 
    5722             : static std::shared_ptr<GDALMDArray>
    5723         466 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
    5724             :                             const std::string &fieldName)
    5725             : {
    5726         466 :     CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
    5727         466 :     const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
    5728         983 :     for (const auto &comp : self->GetDataType().GetComponents())
    5729             :     {
    5730         982 :         if (comp->GetName() == fieldName)
    5731             :         {
    5732         465 :             srcComp = &comp;
    5733         465 :             break;
    5734             :         }
    5735             :     }
    5736         466 :     if (srcComp == nullptr)
    5737             :     {
    5738           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
    5739             :                  fieldName.c_str());
    5740           1 :         return nullptr;
    5741             :     }
    5742         465 :     return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
    5743             : }
    5744             : 
    5745             : /************************************************************************/
    5746             : /*                              GetView()                               */
    5747             : /************************************************************************/
    5748             : 
    5749             : // clang-format off
    5750             : /** Return a view of the array using slicing or field access.
    5751             :  *
    5752             :  * The slice expression uses the same syntax as NumPy basic slicing and
    5753             :  * indexing. See
    5754             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
    5755             :  * Or it can use field access by name. See
    5756             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
    5757             :  *
    5758             :  * Multiple [] bracket elements can be concatenated, with a slice expression
    5759             :  * or field name inside each.
    5760             :  *
    5761             :  * For basic slicing and indexing, inside each [] bracket element, a list of
    5762             :  * indexes that apply to successive source dimensions, can be specified, using
    5763             :  * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
    5764             :  * or newaxis, using a comma separator.
    5765             :  *
    5766             :  * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
    5767             :  * <ul>
    5768             :  * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
    5769             :  *     at index 1 in the first dimension, and index 2 in the second dimension
    5770             :  *     from the source array. That is 5</li>
    5771             :  * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
    5772             :  * implemented internally doing this intermediate slicing approach.</li>
    5773             :  * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
    5774             :  * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
    5775             :  *     first dimension. That is [4,5,6,7].</li>
    5776             :  * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
    5777             :  *     second dimension. That is [2,6].</li>
    5778             :  * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
    5779             :  * the second dimension. That is [[2],[6]].</li>
    5780             :  * <li>GetView("[::,2]"): Same as
    5781             :  * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
    5782             :  * ellipsis only expands to one dimension here.</li>
    5783             :  * <li>GetView("[:,::2]"):
    5784             :  * returns a 2-dimensional array, with even-indexed elements of the second
    5785             :  * dimension. That is [[0,2],[4,6]].</li>
    5786             :  * <li>GetView("[:,1::2]"): returns a
    5787             :  * 2-dimensional array, with odd-indexed elements of the second dimension. That
    5788             :  * is [[1,3],[5,7]].</li>
    5789             :  * <li>GetView("[:,1:3:]"): returns a 2-dimensional
    5790             :  * array, with elements of the second dimension with index in the range [1,3[.
    5791             :  * That is [[1,2],[5,6]].</li>
    5792             :  * <li>GetView("[::-1,:]"): returns a 2-dimensional
    5793             :  * array, with the values in first dimension reversed. That is
    5794             :  * [[4,5,6,7],[0,1,2,3]].</li>
    5795             :  * <li>GetView("[newaxis,...]"): returns a
    5796             :  * 3-dimensional array, with an additional dimension of size 1 put at the
    5797             :  * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
    5798             :  * </ul>
    5799             :  *
    5800             :  * One difference with NumPy behavior is that ranges that would result in
    5801             :  * zero elements are not allowed (dimensions of size 0 not being allowed in the
    5802             :  * GDAL multidimensional model).
    5803             :  *
    5804             :  * For field access, the syntax to use is ["field_name"] or ['field_name'].
    5805             :  * Multiple field specification is not supported currently.
    5806             :  *
    5807             :  * Both type of access can be combined, e.g. GetView("[1]['field_name']")
    5808             :  *
    5809             :  * \note When using the GDAL Python bindings, natural Python syntax can be
    5810             :  * used. That is ar[0,::,1]["foo"] will be internally translated to
    5811             :  * ar.GetView("[0,::,1]['foo']")
    5812             :  * \note When using the C++ API and integer indexing only, you may use the
    5813             :  * at(idx0, idx1, ...) method.
    5814             :  *
    5815             :  * The returned array holds a reference to the original one, and thus is
    5816             :  * a view of it (not a copy). If the content of the original array changes,
    5817             :  * the content of the view array too. When using basic slicing and indexing,
    5818             :  * the view can be written if the underlying array is writable.
    5819             :  *
    5820             :  * This is the same as the C function GDALMDArrayGetView()
    5821             :  *
    5822             :  * @param viewExpr Expression expressing basic slicing and indexing, or field
    5823             :  * access.
    5824             :  * @return a new array, that holds a reference to the original one, and thus is
    5825             :  * a view of it (not a copy), or nullptr in case of error.
    5826             :  */
    5827             : // clang-format on
    5828             : 
    5829             : std::shared_ptr<GDALMDArray>
    5830        1124 : GDALMDArray::GetView(const std::string &viewExpr) const
    5831             : {
    5832        2248 :     std::vector<ViewSpec> viewSpecs;
    5833        2248 :     return GetView(viewExpr, true, viewSpecs);
    5834             : }
    5835             : 
    5836             : //! @cond Doxygen_Suppress
    5837             : std::shared_ptr<GDALMDArray>
    5838        1196 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
    5839             :                      std::vector<ViewSpec> &viewSpecs) const
    5840             : {
    5841        2392 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    5842        1196 :     if (!self)
    5843             :     {
    5844           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    5845             :                  "Driver implementation issue: m_pSelf not set !");
    5846           1 :         return nullptr;
    5847             :     }
    5848        1195 :     std::string curExpr(viewExpr);
    5849             :     while (true)
    5850             :     {
    5851        1198 :         if (curExpr.empty() || curExpr[0] != '[')
    5852             :         {
    5853           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    5854             :                      "Slice string should start with ['");
    5855        1195 :             return nullptr;
    5856             :         }
    5857             : 
    5858        1196 :         std::string fieldName;
    5859             :         size_t endExpr;
    5860        1196 :         if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
    5861             :         {
    5862         470 :             if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
    5863             :             {
    5864           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5865             :                          "Field access not allowed on non-compound data type");
    5866           2 :                 return nullptr;
    5867             :             }
    5868         468 :             size_t idx = 2;
    5869        5119 :             for (; idx < curExpr.size(); idx++)
    5870             :             {
    5871        5118 :                 const char ch = curExpr[idx];
    5872        5118 :                 if (ch == curExpr[1])
    5873         467 :                     break;
    5874        4651 :                 if (ch == '\\' && idx + 1 < curExpr.size())
    5875             :                 {
    5876           1 :                     fieldName += curExpr[idx + 1];
    5877           1 :                     idx++;
    5878             :                 }
    5879             :                 else
    5880             :                 {
    5881        4650 :                     fieldName += ch;
    5882             :                 }
    5883             :             }
    5884         468 :             if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
    5885             :             {
    5886           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5887             :                          "Invalid field access specification");
    5888           2 :                 return nullptr;
    5889             :             }
    5890         466 :             endExpr = idx + 1;
    5891             :         }
    5892             :         else
    5893             :         {
    5894         726 :             endExpr = curExpr.find(']');
    5895             :         }
    5896        1192 :         if (endExpr == std::string::npos)
    5897             :         {
    5898           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
    5899           1 :             return nullptr;
    5900             :         }
    5901        1191 :         if (endExpr == 1)
    5902             :         {
    5903           1 :             CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
    5904           1 :             return nullptr;
    5905             :         }
    5906        1190 :         std::string activeSlice(curExpr.substr(1, endExpr - 1));
    5907             : 
    5908        1190 :         if (!fieldName.empty())
    5909             :         {
    5910         932 :             ViewSpec viewSpec;
    5911         466 :             viewSpec.m_osFieldName = fieldName;
    5912         466 :             viewSpecs.emplace_back(std::move(viewSpec));
    5913             :         }
    5914             : 
    5915        1190 :         auto newArray = !fieldName.empty()
    5916             :                             ? CreateFieldNameExtractArray(self, fieldName)
    5917             :                             : CreateSlicedArray(self, viewExpr, activeSlice,
    5918        1190 :                                                 bRenameDimensions, viewSpecs);
    5919             : 
    5920        1190 :         if (endExpr == curExpr.size() - 1)
    5921             :         {
    5922        1187 :             return newArray;
    5923             :         }
    5924           3 :         self = std::move(newArray);
    5925           3 :         curExpr = curExpr.substr(endExpr + 1);
    5926           3 :     }
    5927             : }
    5928             : 
    5929             : //! @endcond
    5930             : 
    5931             : std::shared_ptr<GDALMDArray>
    5932          19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
    5933             : {
    5934          19 :     std::string osExpr("[");
    5935          19 :     bool bFirst = true;
    5936          45 :     for (const auto &idx : indices)
    5937             :     {
    5938          26 :         if (!bFirst)
    5939           7 :             osExpr += ',';
    5940          26 :         bFirst = false;
    5941          26 :         osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
    5942             :     }
    5943          57 :     return GetView(osExpr + ']');
    5944             : }
    5945             : 
    5946             : /************************************************************************/
    5947             : /*                              operator[]                              */
    5948             : /************************************************************************/
    5949             : 
    5950             : /** Return a view of the array using field access
    5951             :  *
    5952             :  * Equivalent of GetView("['fieldName']")
    5953             :  *
    5954             :  * \note When operating on a shared_ptr, use (*array)["fieldName"] syntax.
    5955             :  */
    5956             : std::shared_ptr<GDALMDArray>
    5957           2 : GDALMDArray::operator[](const std::string &fieldName) const
    5958             : {
    5959           2 :     return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
    5960           4 :                                             .replaceAll('\\', "\\\\")
    5961           4 :                                             .replaceAll('\'', "\\\'")
    5962           6 :                                             .c_str()));
    5963             : }
    5964             : 
    5965             : /************************************************************************/
    5966             : /*                        GDALMDArrayTransposed                         */
    5967             : /************************************************************************/
    5968             : 
    5969             : class GDALMDArrayTransposed final : public GDALPamMDArray
    5970             : {
    5971             :   private:
    5972             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5973             :     std::vector<int> m_anMapNewAxisToOldAxis{};
    5974             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    5975             : 
    5976             :     mutable std::vector<GUInt64> m_parentStart;
    5977             :     mutable std::vector<size_t> m_parentCount;
    5978             :     mutable std::vector<GInt64> m_parentStep;
    5979             :     mutable std::vector<GPtrDiff_t> m_parentStride;
    5980             : 
    5981             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
    5982             :                              const GInt64 *arrayStep,
    5983             :                              const GPtrDiff_t *bufferStride) const;
    5984             : 
    5985             :     static std::string
    5986          84 :     MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
    5987             :     {
    5988          84 :         std::string ret;
    5989          84 :         ret += '[';
    5990         312 :         for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
    5991             :         {
    5992         228 :             if (i > 0)
    5993         144 :                 ret += ',';
    5994         228 :             ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
    5995             :         }
    5996          84 :         ret += ']';
    5997          84 :         return ret;
    5998             :     }
    5999             : 
    6000             :   protected:
    6001          42 :     GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
    6002             :                           const std::vector<int> &anMapNewAxisToOldAxis,
    6003             :                           std::vector<std::shared_ptr<GDALDimension>> &&dims)
    6004          84 :         : GDALAbstractMDArray(std::string(),
    6005          84 :                               "Transposed view of " + poParent->GetFullName() +
    6006          84 :                                   " along " +
    6007          42 :                                   MappingToStr(anMapNewAxisToOldAxis)),
    6008          84 :           GDALPamMDArray(std::string(),
    6009          84 :                          "Transposed view of " + poParent->GetFullName() +
    6010         168 :                              " along " + MappingToStr(anMapNewAxisToOldAxis),
    6011          84 :                          GDALPamMultiDim::GetPAM(poParent),
    6012             :                          poParent->GetContext()),
    6013          42 :           m_poParent(std::move(poParent)),
    6014             :           m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
    6015          42 :           m_dims(std::move(dims)),
    6016          42 :           m_parentStart(m_poParent->GetDimensionCount()),
    6017          42 :           m_parentCount(m_poParent->GetDimensionCount()),
    6018          42 :           m_parentStep(m_poParent->GetDimensionCount()),
    6019         336 :           m_parentStride(m_poParent->GetDimensionCount())
    6020             :     {
    6021          42 :     }
    6022             : 
    6023             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6024             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    6025             :                const GDALExtendedDataType &bufferDataType,
    6026             :                void *pDstBuffer) const override;
    6027             : 
    6028             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    6029             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    6030             :                 const GDALExtendedDataType &bufferDataType,
    6031             :                 const void *pSrcBuffer) override;
    6032             : 
    6033             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6034             :                      CSLConstList papszOptions) const override;
    6035             : 
    6036             :   public:
    6037             :     static std::shared_ptr<GDALMDArrayTransposed>
    6038          42 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    6039             :            const std::vector<int> &anMapNewAxisToOldAxis)
    6040             :     {
    6041          42 :         const auto &parentDims(poParent->GetDimensions());
    6042          84 :         std::vector<std::shared_ptr<GDALDimension>> dims;
    6043         156 :         for (const auto iOldAxis : anMapNewAxisToOldAxis)
    6044             :         {
    6045         114 :             if (iOldAxis < 0)
    6046             :             {
    6047           1 :                 dims.push_back(std::make_shared<GDALDimension>(
    6048           2 :                     std::string(), "newaxis", std::string(), std::string(), 1));
    6049             :             }
    6050             :             else
    6051             :             {
    6052         113 :                 dims.emplace_back(parentDims[iOldAxis]);
    6053             :             }
    6054             :         }
    6055             : 
    6056             :         auto newAr(
    6057             :             std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
    6058          42 :                 poParent, anMapNewAxisToOldAxis, std::move(dims))));
    6059          42 :         newAr->SetSelf(newAr);
    6060          84 :         return newAr;
    6061             :     }
    6062             : 
    6063           1 :     bool IsWritable() const override
    6064             :     {
    6065           1 :         return m_poParent->IsWritable();
    6066             :     }
    6067             : 
    6068          84 :     const std::string &GetFilename() const override
    6069             :     {
    6070          84 :         return m_poParent->GetFilename();
    6071             :     }
    6072             : 
    6073             :     const std::vector<std::shared_ptr<GDALDimension>> &
    6074         358 :     GetDimensions() const override
    6075             :     {
    6076         358 :         return m_dims;
    6077             :     }
    6078             : 
    6079         141 :     const GDALExtendedDataType &GetDataType() const override
    6080             :     {
    6081         141 :         return m_poParent->GetDataType();
    6082             :     }
    6083             : 
    6084           4 :     const std::string &GetUnit() const override
    6085             :     {
    6086           4 :         return m_poParent->GetUnit();
    6087             :     }
    6088             : 
    6089           5 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    6090             :     {
    6091          10 :         auto poSrcSRS = m_poParent->GetSpatialRef();
    6092           5 :         if (!poSrcSRS)
    6093           2 :             return nullptr;
    6094           6 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
    6095           6 :         std::vector<int> dstMapping;
    6096           9 :         for (int srcAxis : srcMapping)
    6097             :         {
    6098           6 :             bool bFound = false;
    6099          14 :             for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
    6100             :             {
    6101          14 :                 if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
    6102             :                 {
    6103           6 :                     dstMapping.push_back(static_cast<int>(i) + 1);
    6104           6 :                     bFound = true;
    6105           6 :                     break;
    6106             :                 }
    6107             :             }
    6108           6 :             if (!bFound)
    6109             :             {
    6110           0 :                 dstMapping.push_back(0);
    6111             :             }
    6112             :         }
    6113           6 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
    6114           3 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
    6115           3 :         return poClone;
    6116             :     }
    6117             : 
    6118           4 :     const void *GetRawNoDataValue() const override
    6119             :     {
    6120           4 :         return m_poParent->GetRawNoDataValue();
    6121             :     }
    6122             : 
    6123             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
    6124             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
    6125             : 
    6126           4 :     double GetOffset(bool *pbHasOffset,
    6127             :                      GDALDataType *peStorageType) const override
    6128             :     {
    6129           4 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    6130             :     }
    6131             : 
    6132           4 :     double GetScale(bool *pbHasScale,
    6133             :                     GDALDataType *peStorageType) const override
    6134             :     {
    6135           4 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    6136             :     }
    6137             : 
    6138             :     // bool SetOffset(double dfOffset) override { return
    6139             :     // m_poParent->SetOffset(dfOffset); }
    6140             : 
    6141             :     // bool SetScale(double dfScale) override { return
    6142             :     // m_poParent->SetScale(dfScale); }
    6143             : 
    6144           3 :     std::vector<GUInt64> GetBlockSize() const override
    6145             :     {
    6146           3 :         std::vector<GUInt64> ret(GetDimensionCount());
    6147           6 :         const auto parentBlockSize(m_poParent->GetBlockSize());
    6148          11 :         for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
    6149             :         {
    6150           8 :             const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
    6151           8 :             if (iOldAxis >= 0)
    6152             :             {
    6153           7 :                 ret[i] = parentBlockSize[iOldAxis];
    6154             :             }
    6155             :         }
    6156           6 :         return ret;
    6157             :     }
    6158             : 
    6159             :     std::shared_ptr<GDALAttribute>
    6160           1 :     GetAttribute(const std::string &osName) const override
    6161             :     {
    6162           1 :         return m_poParent->GetAttribute(osName);
    6163             :     }
    6164             : 
    6165             :     std::vector<std::shared_ptr<GDALAttribute>>
    6166           6 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    6167             :     {
    6168           6 :         return m_poParent->GetAttributes(papszOptions);
    6169             :     }
    6170             : };
    6171             : 
    6172             : /************************************************************************/
    6173             : /*                        PrepareParentArrays()                         */
    6174             : /************************************************************************/
    6175             : 
    6176          47 : void GDALMDArrayTransposed::PrepareParentArrays(
    6177             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    6178             :     const GPtrDiff_t *bufferStride) const
    6179             : {
    6180         176 :     for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
    6181             :     {
    6182         129 :         const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
    6183         129 :         if (iOldAxis >= 0)
    6184             :         {
    6185         128 :             m_parentStart[iOldAxis] = arrayStartIdx[i];
    6186         128 :             m_parentCount[iOldAxis] = count[i];
    6187         128 :             if (arrayStep)  // only null when called from IAdviseRead()
    6188             :             {
    6189         126 :                 m_parentStep[iOldAxis] = arrayStep[i];
    6190             :             }
    6191         128 :             if (bufferStride)  // only null when called from IAdviseRead()
    6192             :             {
    6193         126 :                 m_parentStride[iOldAxis] = bufferStride[i];
    6194             :             }
    6195             :         }
    6196             :     }
    6197          47 : }
    6198             : 
    6199             : /************************************************************************/
    6200             : /*                               IRead()                                */
    6201             : /************************************************************************/
    6202             : 
    6203          44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
    6204             :                                   const size_t *count, const GInt64 *arrayStep,
    6205             :                                   const GPtrDiff_t *bufferStride,
    6206             :                                   const GDALExtendedDataType &bufferDataType,
    6207             :                                   void *pDstBuffer) const
    6208             : {
    6209          44 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    6210          88 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
    6211          44 :                             m_parentStep.data(), m_parentStride.data(),
    6212          44 :                             bufferDataType, pDstBuffer);
    6213             : }
    6214             : 
    6215             : /************************************************************************/
    6216             : /*                               IWrite()                               */
    6217             : /************************************************************************/
    6218             : 
    6219           2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
    6220             :                                    const size_t *count, const GInt64 *arrayStep,
    6221             :                                    const GPtrDiff_t *bufferStride,
    6222             :                                    const GDALExtendedDataType &bufferDataType,
    6223             :                                    const void *pSrcBuffer)
    6224             : {
    6225           2 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    6226           4 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
    6227           2 :                              m_parentStep.data(), m_parentStride.data(),
    6228           2 :                              bufferDataType, pSrcBuffer);
    6229             : }
    6230             : 
    6231             : /************************************************************************/
    6232             : /*                            IAdviseRead()                             */
    6233             : /************************************************************************/
    6234             : 
    6235           1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
    6236             :                                         const size_t *count,
    6237             :                                         CSLConstList papszOptions) const
    6238             : {
    6239           1 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
    6240           1 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
    6241           1 :                                   papszOptions);
    6242             : }
    6243             : 
    6244             : /************************************************************************/
    6245             : /*                             Transpose()                              */
    6246             : /************************************************************************/
    6247             : 
    6248             : /** Return a view of the array whose axis have been reordered.
    6249             :  *
    6250             :  * The anMapNewAxisToOldAxis parameter should contain all the values between 0
    6251             :  * and GetDimensionCount() - 1, and each only once.
    6252             :  * -1 can be used as a special index value to ask for the insertion of a new
    6253             :  * axis of size 1.
    6254             :  * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
    6255             :  * index of one of its dimension, it corresponds to the axis of index
    6256             :  * anMapNewAxisToOldAxis[i] from the current array.
    6257             :  *
    6258             :  * This is similar to the numpy.transpose() method
    6259             :  *
    6260             :  * The returned array holds a reference to the original one, and thus is
    6261             :  * a view of it (not a copy). If the content of the original array changes,
    6262             :  * the content of the view array too. The view can be written if the underlying
    6263             :  * array is writable.
    6264             :  *
    6265             :  * Note that I/O performance in such a transposed view might be poor.
    6266             :  *
    6267             :  * This is the same as the C function GDALMDArrayTranspose().
    6268             :  *
    6269             :  * @return a new array, that holds a reference to the original one, and thus is
    6270             :  * a view of it (not a copy), or nullptr in case of error.
    6271             :  */
    6272             : std::shared_ptr<GDALMDArray>
    6273          50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
    6274             : {
    6275         100 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    6276          50 :     if (!self)
    6277             :     {
    6278           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6279             :                  "Driver implementation issue: m_pSelf not set !");
    6280           0 :         return nullptr;
    6281             :     }
    6282          50 :     const int nDims = static_cast<int>(GetDimensionCount());
    6283         100 :     std::vector<bool> alreadyUsedOldAxis(nDims, false);
    6284          50 :     int nCountOldAxis = 0;
    6285         179 :     for (const auto iOldAxis : anMapNewAxisToOldAxis)
    6286             :     {
    6287         133 :         if (iOldAxis < -1 || iOldAxis >= nDims)
    6288             :         {
    6289           3 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
    6290           4 :             return nullptr;
    6291             :         }
    6292         130 :         if (iOldAxis >= 0)
    6293             :         {
    6294         128 :             if (alreadyUsedOldAxis[iOldAxis])
    6295             :             {
    6296           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
    6297             :                          iOldAxis);
    6298           1 :                 return nullptr;
    6299             :             }
    6300         127 :             alreadyUsedOldAxis[iOldAxis] = true;
    6301         127 :             nCountOldAxis++;
    6302             :         }
    6303             :     }
    6304          46 :     if (nCountOldAxis != nDims)
    6305             :     {
    6306           4 :         CPLError(CE_Failure, CPLE_AppDefined,
    6307             :                  "One or several original axis missing");
    6308           4 :         return nullptr;
    6309             :     }
    6310          42 :     return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
    6311             : }
    6312             : 
    6313             : /************************************************************************/
    6314             : /*                               IRead()                                */
    6315             : /************************************************************************/
    6316             : 
    6317          16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
    6318             :                                 const size_t *count, const GInt64 *arrayStep,
    6319             :                                 const GPtrDiff_t *bufferStride,
    6320             :                                 const GDALExtendedDataType &bufferDataType,
    6321             :                                 void *pDstBuffer) const
    6322             : {
    6323          16 :     const double dfScale = m_dfScale;
    6324          16 :     const double dfOffset = m_dfOffset;
    6325          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
    6326             :     const auto dtDouble =
    6327          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
    6328          16 :     const size_t nDTSize = dtDouble.GetSize();
    6329          16 :     const bool bTempBufferNeeded = (dtDouble != bufferDataType);
    6330             : 
    6331          16 :     double adfSrcNoData[2] = {0, 0};
    6332          16 :     if (m_bHasNoData)
    6333             :     {
    6334           9 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
    6335           9 :                                         m_poParent->GetDataType(),
    6336             :                                         &adfSrcNoData[0], dtDouble);
    6337             :     }
    6338             : 
    6339          16 :     const auto nDims = GetDimensions().size();
    6340          16 :     if (nDims == 0)
    6341             :     {
    6342             :         double adfVal[2];
    6343           9 :         if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
    6344             :                               dtDouble, &adfVal[0]))
    6345             :         {
    6346           0 :             return false;
    6347             :         }
    6348           9 :         if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
    6349             :         {
    6350           6 :             adfVal[0] = adfVal[0] * dfScale + dfOffset;
    6351           6 :             if (bDTIsComplex)
    6352             :             {
    6353           2 :                 adfVal[1] = adfVal[1] * dfScale + dfOffset;
    6354             :             }
    6355           6 :             GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
    6356             :                                             bufferDataType);
    6357             :         }
    6358             :         else
    6359             :         {
    6360           3 :             GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
    6361             :                                             pDstBuffer, bufferDataType);
    6362             :         }
    6363           9 :         return true;
    6364             :     }
    6365             : 
    6366          14 :     std::vector<GPtrDiff_t> actualBufferStrideVector;
    6367           7 :     const GPtrDiff_t *actualBufferStridePtr = bufferStride;
    6368           7 :     void *pTempBuffer = pDstBuffer;
    6369           7 :     if (bTempBufferNeeded)
    6370             :     {
    6371           2 :         size_t nElts = 1;
    6372           2 :         actualBufferStrideVector.resize(nDims);
    6373           7 :         for (size_t i = 0; i < nDims; i++)
    6374           5 :             nElts *= count[i];
    6375           2 :         actualBufferStrideVector.back() = 1;
    6376           5 :         for (size_t i = nDims - 1; i > 0;)
    6377             :         {
    6378           3 :             --i;
    6379           3 :             actualBufferStrideVector[i] =
    6380           3 :                 actualBufferStrideVector[i + 1] * count[i + 1];
    6381             :         }
    6382           2 :         actualBufferStridePtr = actualBufferStrideVector.data();
    6383           2 :         pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
    6384           2 :         if (!pTempBuffer)
    6385           0 :             return false;
    6386             :     }
    6387           7 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
    6388             :                           actualBufferStridePtr, dtDouble, pTempBuffer))
    6389             :     {
    6390           0 :         if (bTempBufferNeeded)
    6391           0 :             VSIFree(pTempBuffer);
    6392           0 :         return false;
    6393             :     }
    6394             : 
    6395             :     struct Stack
    6396             :     {
    6397             :         size_t nIters = 0;
    6398             :         double *src_ptr = nullptr;
    6399             :         GByte *dst_ptr = nullptr;
    6400             :         GPtrDiff_t src_inc_offset = 0;
    6401             :         GPtrDiff_t dst_inc_offset = 0;
    6402             :     };
    6403             : 
    6404           7 :     std::vector<Stack> stack(nDims);
    6405           7 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    6406          23 :     for (size_t i = 0; i < nDims; i++)
    6407             :     {
    6408          32 :         stack[i].src_inc_offset =
    6409          16 :             actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
    6410          16 :         stack[i].dst_inc_offset =
    6411          16 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6412             :     }
    6413           7 :     stack[0].src_ptr = static_cast<double *>(pTempBuffer);
    6414           7 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    6415             : 
    6416           7 :     size_t dimIdx = 0;
    6417           7 :     const size_t nDimsMinus1 = nDims - 1;
    6418             :     GByte abyDstNoData[16];
    6419           7 :     CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
    6420           7 :     GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
    6421             :                                     bufferDataType);
    6422             : 
    6423          37 : lbl_next_depth:
    6424          37 :     if (dimIdx == nDimsMinus1)
    6425             :     {
    6426          25 :         auto nIters = count[dimIdx];
    6427          25 :         double *padfVal = stack[dimIdx].src_ptr;
    6428          25 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
    6429             :         while (true)
    6430             :         {
    6431          92 :             if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
    6432             :             {
    6433          88 :                 padfVal[0] = padfVal[0] * dfScale + dfOffset;
    6434          88 :                 if (bDTIsComplex)
    6435             :                 {
    6436           1 :                     padfVal[1] = padfVal[1] * dfScale + dfOffset;
    6437             :                 }
    6438          88 :                 if (bTempBufferNeeded)
    6439             :                 {
    6440          29 :                     GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
    6441             :                                                     dst_ptr, bufferDataType);
    6442             :                 }
    6443             :             }
    6444             :             else
    6445             :             {
    6446           4 :                 memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
    6447             :             }
    6448             : 
    6449          92 :             if ((--nIters) == 0)
    6450          25 :                 break;
    6451          67 :             padfVal += stack[dimIdx].src_inc_offset;
    6452          67 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    6453             :         }
    6454             :     }
    6455             :     else
    6456             :     {
    6457          12 :         stack[dimIdx].nIters = count[dimIdx];
    6458             :         while (true)
    6459             :         {
    6460          30 :             dimIdx++;
    6461          30 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    6462          30 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6463          30 :             goto lbl_next_depth;
    6464          30 :         lbl_return_to_caller:
    6465          30 :             dimIdx--;
    6466          30 :             if ((--stack[dimIdx].nIters) == 0)
    6467          12 :                 break;
    6468          18 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    6469          18 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6470             :         }
    6471             :     }
    6472          37 :     if (dimIdx > 0)
    6473          30 :         goto lbl_return_to_caller;
    6474             : 
    6475           7 :     if (bTempBufferNeeded)
    6476           2 :         VSIFree(pTempBuffer);
    6477           7 :     return true;
    6478             : }
    6479             : 
    6480             : /************************************************************************/
    6481             : /*                               IWrite()                               */
    6482             : /************************************************************************/
    6483             : 
    6484          16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
    6485             :                                  const size_t *count, const GInt64 *arrayStep,
    6486             :                                  const GPtrDiff_t *bufferStride,
    6487             :                                  const GDALExtendedDataType &bufferDataType,
    6488             :                                  const void *pSrcBuffer)
    6489             : {
    6490          16 :     const double dfScale = m_dfScale;
    6491          16 :     const double dfOffset = m_dfOffset;
    6492          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
    6493             :     const auto dtDouble =
    6494          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
    6495          16 :     const size_t nDTSize = dtDouble.GetSize();
    6496          16 :     const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
    6497             :     const bool bSelfAndParentHaveNoData =
    6498          16 :         m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
    6499          16 :     double dfNoData = 0;
    6500          16 :     if (m_bHasNoData)
    6501             :     {
    6502           7 :         GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
    6503             :                         &dfNoData, GDT_Float64, 0, 1);
    6504             :     }
    6505             : 
    6506          16 :     double adfSrcNoData[2] = {0, 0};
    6507          16 :     if (bSelfAndParentHaveNoData)
    6508             :     {
    6509           7 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
    6510           7 :                                         m_poParent->GetDataType(),
    6511             :                                         &adfSrcNoData[0], dtDouble);
    6512             :     }
    6513             : 
    6514          16 :     const auto nDims = GetDimensions().size();
    6515          16 :     if (nDims == 0)
    6516             :     {
    6517             :         double adfVal[2];
    6518          10 :         GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
    6519             :                                         dtDouble);
    6520          16 :         if (bSelfAndParentHaveNoData &&
    6521           6 :             (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
    6522             :         {
    6523           4 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
    6524           2 :                                      bufferStride, m_poParent->GetDataType(),
    6525           4 :                                      m_poParent->GetRawNoDataValue());
    6526             :         }
    6527             :         else
    6528             :         {
    6529           8 :             adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
    6530           8 :             if (bDTIsComplex)
    6531             :             {
    6532           2 :                 adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
    6533             :             }
    6534           8 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
    6535           8 :                                      bufferStride, dtDouble, &adfVal[0]);
    6536             :         }
    6537             :     }
    6538             : 
    6539          12 :     std::vector<GPtrDiff_t> tmpBufferStrideVector;
    6540           6 :     size_t nElts = 1;
    6541           6 :     tmpBufferStrideVector.resize(nDims);
    6542          20 :     for (size_t i = 0; i < nDims; i++)
    6543          14 :         nElts *= count[i];
    6544           6 :     tmpBufferStrideVector.back() = 1;
    6545          14 :     for (size_t i = nDims - 1; i > 0;)
    6546             :     {
    6547           8 :         --i;
    6548           8 :         tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
    6549             :     }
    6550           6 :     const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
    6551           6 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
    6552           6 :     if (!pTempBuffer)
    6553           0 :         return false;
    6554             : 
    6555             :     struct Stack
    6556             :     {
    6557             :         size_t nIters = 0;
    6558             :         double *dst_ptr = nullptr;
    6559             :         const GByte *src_ptr = nullptr;
    6560             :         GPtrDiff_t src_inc_offset = 0;
    6561             :         GPtrDiff_t dst_inc_offset = 0;
    6562             :     };
    6563             : 
    6564           6 :     std::vector<Stack> stack(nDims);
    6565           6 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    6566          20 :     for (size_t i = 0; i < nDims; i++)
    6567             :     {
    6568          28 :         stack[i].dst_inc_offset =
    6569          14 :             tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
    6570          14 :         stack[i].src_inc_offset =
    6571          14 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6572             :     }
    6573           6 :     stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
    6574           6 :     stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
    6575             : 
    6576           6 :     size_t dimIdx = 0;
    6577           6 :     const size_t nDimsMinus1 = nDims - 1;
    6578             : 
    6579          34 : lbl_next_depth:
    6580          34 :     if (dimIdx == nDimsMinus1)
    6581             :     {
    6582          23 :         auto nIters = count[dimIdx];
    6583          23 :         double *dst_ptr = stack[dimIdx].dst_ptr;
    6584          23 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
    6585             :         while (true)
    6586             :         {
    6587             :             double adfVal[2];
    6588             :             const double *padfSrcVal;
    6589          86 :             if (bIsBufferDataTypeNativeDataType)
    6590             :             {
    6591          50 :                 padfSrcVal = reinterpret_cast<const double *>(src_ptr);
    6592             :             }
    6593             :             else
    6594             :             {
    6595          36 :                 GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
    6596             :                                                 &adfVal[0], dtDouble);
    6597          36 :                 padfSrcVal = adfVal;
    6598             :             }
    6599             : 
    6600         148 :             if (bSelfAndParentHaveNoData &&
    6601          62 :                 (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
    6602             :             {
    6603           3 :                 dst_ptr[0] = adfSrcNoData[0];
    6604           3 :                 if (bDTIsComplex)
    6605             :                 {
    6606           1 :                     dst_ptr[1] = adfSrcNoData[1];
    6607             :                 }
    6608             :             }
    6609             :             else
    6610             :             {
    6611          83 :                 dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
    6612          83 :                 if (bDTIsComplex)
    6613             :                 {
    6614           1 :                     dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
    6615             :                 }
    6616             :             }
    6617             : 
    6618          86 :             if ((--nIters) == 0)
    6619          23 :                 break;
    6620          63 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    6621          63 :             src_ptr += stack[dimIdx].src_inc_offset;
    6622          63 :         }
    6623             :     }
    6624             :     else
    6625             :     {
    6626          11 :         stack[dimIdx].nIters = count[dimIdx];
    6627             :         while (true)
    6628             :         {
    6629          28 :             dimIdx++;
    6630          28 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    6631          28 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6632          28 :             goto lbl_next_depth;
    6633          28 :         lbl_return_to_caller:
    6634          28 :             dimIdx--;
    6635          28 :             if ((--stack[dimIdx].nIters) == 0)
    6636          11 :                 break;
    6637          17 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    6638          17 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6639             :         }
    6640             :     }
    6641          34 :     if (dimIdx > 0)
    6642          28 :         goto lbl_return_to_caller;
    6643             : 
    6644             :     // If the parent array is not double/complex-double, then convert the
    6645             :     // values to it, before calling Write(), as some implementations can be
    6646             :     // very slow when doing the type conversion.
    6647           6 :     const auto &eParentDT = m_poParent->GetDataType();
    6648           6 :     const size_t nParentDTSize = eParentDT.GetSize();
    6649           6 :     if (nParentDTSize <= nDTSize / 2)
    6650             :     {
    6651             :         // Copy in-place by making sure that source and target do not overlap
    6652           6 :         const auto eNumericDT = dtDouble.GetNumericDataType();
    6653           6 :         const auto eParentNumericDT = eParentDT.GetNumericDataType();
    6654             : 
    6655             :         // Copy first element
    6656             :         {
    6657           6 :             std::vector<GByte> abyTemp(nParentDTSize);
    6658           6 :             GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
    6659           6 :                             static_cast<int>(nDTSize), &abyTemp[0],
    6660             :                             eParentNumericDT, static_cast<int>(nParentDTSize),
    6661             :                             1);
    6662           6 :             memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
    6663             :         }
    6664             :         // Remaining elements
    6665          86 :         for (size_t i = 1; i < nElts; ++i)
    6666             :         {
    6667          80 :             GDALCopyWords64(
    6668          80 :                 static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
    6669          80 :                 static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
    6670             :                 eParentNumericDT, 0, 1);
    6671             :         }
    6672             :     }
    6673             : 
    6674             :     const bool ret =
    6675           6 :         m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
    6676             :                           eParentDT, pTempBuffer);
    6677             : 
    6678           6 :     VSIFree(pTempBuffer);
    6679           6 :     return ret;
    6680             : }
    6681             : 
    6682             : /************************************************************************/
    6683             : /*                            GetUnscaled()                             */
    6684             : /************************************************************************/
    6685             : 
    6686             : /** Return an array that is the unscaled version of the current one.
    6687             :  *
    6688             :  * That is each value of the unscaled array will be
    6689             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    6690             :  *
    6691             :  * Starting with GDAL 3.3, the Write() method is implemented and will convert
    6692             :  * from unscaled values to raw values.
    6693             :  *
    6694             :  * This is the same as the C function GDALMDArrayGetUnscaled().
    6695             :  *
    6696             :  * @param dfOverriddenScale Custom scale value instead of GetScale()
    6697             :  * @param dfOverriddenOffset Custom offset value instead of GetOffset()
    6698             :  * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
    6699             :  * @return a new array, that holds a reference to the original one, and thus is
    6700             :  * a view of it (not a copy), or nullptr in case of error.
    6701             :  */
    6702             : std::shared_ptr<GDALMDArray>
    6703          17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
    6704             :                          double dfOverriddenDstNodata) const
    6705             : {
    6706          34 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    6707          17 :     if (!self)
    6708             :     {
    6709           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6710             :                  "Driver implementation issue: m_pSelf not set !");
    6711           0 :         return nullptr;
    6712             :     }
    6713          17 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    6714             :     {
    6715           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6716             :                  "GetUnscaled() only supports numeric data type");
    6717           0 :         return nullptr;
    6718             :     }
    6719             :     const double dfScale =
    6720          17 :         std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
    6721             :     const double dfOffset =
    6722          17 :         std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
    6723          17 :     if (dfScale == 1.0 && dfOffset == 0.0)
    6724           4 :         return self;
    6725             : 
    6726          13 :     GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
    6727          13 :                            ? GDT_CFloat64
    6728          13 :                            : GDT_Float64;
    6729          13 :     if (dfOverriddenScale == -1 && dfOverriddenOffset == 0)
    6730             :     {
    6731           1 :         if (GetDataType().GetNumericDataType() == GDT_Float16)
    6732           0 :             eDT = GDT_Float16;
    6733           1 :         if (GetDataType().GetNumericDataType() == GDT_Float32)
    6734           1 :             eDT = GDT_Float32;
    6735             :     }
    6736             : 
    6737          26 :     return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
    6738          13 :                                        dfOverriddenDstNodata, eDT);
    6739             : }
    6740             : 
    6741             : /************************************************************************/
    6742             : /*                           GDALMDArrayMask                            */
    6743             : /************************************************************************/
    6744             : 
    6745             : class GDALMDArrayMask final : public GDALPamMDArray
    6746             : {
    6747             :   private:
    6748             :     std::shared_ptr<GDALMDArray> m_poParent{};
    6749             :     GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_UInt8)};
    6750             :     double m_dfMissingValue = 0.0;
    6751             :     bool m_bHasMissingValue = false;
    6752             :     double m_dfFillValue = 0.0;
    6753             :     bool m_bHasFillValue = false;
    6754             :     double m_dfValidMin = 0.0;
    6755             :     bool m_bHasValidMin = false;
    6756             :     double m_dfValidMax = 0.0;
    6757             :     bool m_bHasValidMax = false;
    6758             :     std::vector<uint32_t> m_anValidFlagMasks{};
    6759             :     std::vector<uint32_t> m_anValidFlagValues{};
    6760             : 
    6761             :     bool Init(CSLConstList papszOptions);
    6762             : 
    6763             :     template <typename Type>
    6764             :     void
    6765             :     ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
    6766             :                  const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    6767             :                  const void *pTempBuffer,
    6768             :                  const GDALExtendedDataType &oTmpBufferDT,
    6769             :                  const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
    6770             : 
    6771             :   protected:
    6772          48 :     explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
    6773          96 :         : GDALAbstractMDArray(std::string(),
    6774          96 :                               "Mask of " + poParent->GetFullName()),
    6775          96 :           GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
    6776          96 :                          GDALPamMultiDim::GetPAM(poParent),
    6777             :                          poParent->GetContext()),
    6778         240 :           m_poParent(std::move(poParent))
    6779             :     {
    6780          48 :     }
    6781             : 
    6782             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6783             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    6784             :                const GDALExtendedDataType &bufferDataType,
    6785             :                void *pDstBuffer) const override;
    6786             : 
    6787           0 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6788             :                      CSLConstList papszOptions) const override
    6789             :     {
    6790           0 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
    6791             :     }
    6792             : 
    6793             :   public:
    6794             :     static std::shared_ptr<GDALMDArrayMask>
    6795             :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    6796             :            CSLConstList papszOptions);
    6797             : 
    6798           1 :     bool IsWritable() const override
    6799             :     {
    6800           1 :         return false;
    6801             :     }
    6802             : 
    6803          54 :     const std::string &GetFilename() const override
    6804             :     {
    6805          54 :         return m_poParent->GetFilename();
    6806             :     }
    6807             : 
    6808             :     const std::vector<std::shared_ptr<GDALDimension>> &
    6809         382 :     GetDimensions() const override
    6810             :     {
    6811         382 :         return m_poParent->GetDimensions();
    6812             :     }
    6813             : 
    6814         138 :     const GDALExtendedDataType &GetDataType() const override
    6815             :     {
    6816         138 :         return m_dt;
    6817             :     }
    6818             : 
    6819           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    6820             :     {
    6821           1 :         return m_poParent->GetSpatialRef();
    6822             :     }
    6823             : 
    6824           2 :     std::vector<GUInt64> GetBlockSize() const override
    6825             :     {
    6826           2 :         return m_poParent->GetBlockSize();
    6827             :     }
    6828             : };
    6829             : 
    6830             : /************************************************************************/
    6831             : /*                      GDALMDArrayMask::Create()                       */
    6832             : /************************************************************************/
    6833             : 
    6834             : /* static */ std::shared_ptr<GDALMDArrayMask>
    6835          48 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
    6836             :                         CSLConstList papszOptions)
    6837             : {
    6838          96 :     auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
    6839          48 :     newAr->SetSelf(newAr);
    6840          48 :     if (!newAr->Init(papszOptions))
    6841           6 :         return nullptr;
    6842          42 :     return newAr;
    6843             : }
    6844             : 
    6845             : /************************************************************************/
    6846             : /*                       GDALMDArrayMask::Init()                        */
    6847             : /************************************************************************/
    6848             : 
    6849          48 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
    6850             : {
    6851             :     const auto GetSingleValNumericAttr =
    6852         192 :         [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
    6853             :     {
    6854         576 :         auto poAttr = m_poParent->GetAttribute(pszAttrName);
    6855         192 :         if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
    6856             :         {
    6857          22 :             const auto anDimSizes = poAttr->GetDimensionsSize();
    6858          21 :             if (anDimSizes.empty() ||
    6859          10 :                 (anDimSizes.size() == 1 && anDimSizes[0] == 1))
    6860             :             {
    6861          11 :                 bHasVal = true;
    6862          11 :                 dfVal = poAttr->ReadAsDouble();
    6863             :             }
    6864             :         }
    6865         192 :     };
    6866             : 
    6867          48 :     GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
    6868          48 :                             m_dfMissingValue);
    6869          48 :     GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
    6870          48 :     GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
    6871          48 :     GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
    6872             : 
    6873             :     {
    6874         144 :         auto poValidRange = m_poParent->GetAttribute("valid_range");
    6875          54 :         if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
    6876          60 :             poValidRange->GetDimensionsSize()[0] == 2 &&
    6877           6 :             poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
    6878             :         {
    6879           6 :             m_bHasValidMin = true;
    6880           6 :             m_bHasValidMax = true;
    6881           6 :             auto vals = poValidRange->ReadAsDoubleArray();
    6882           6 :             CPLAssert(vals.size() == 2);
    6883           6 :             m_dfValidMin = vals[0];
    6884           6 :             m_dfValidMax = vals[1];
    6885             :         }
    6886             :     }
    6887             : 
    6888             :     // Take into account
    6889             :     // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
    6890             :     // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
    6891             :     const char *pszUnmaskFlags =
    6892          48 :         CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
    6893          48 :     if (pszUnmaskFlags)
    6894             :     {
    6895             :         const auto IsScalarStringAttr =
    6896          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
    6897             :         {
    6898          26 :             return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
    6899          26 :                    (poAttr->GetDimensionsSize().empty() ||
    6900          13 :                     (poAttr->GetDimensionsSize().size() == 1 &&
    6901          26 :                      poAttr->GetDimensionsSize()[0] == 1));
    6902             :         };
    6903             : 
    6904          28 :         auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
    6905          14 :         if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
    6906             :         {
    6907           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6908             :                      "UNMASK_FLAGS option specified but array has no "
    6909             :                      "flag_meanings attribute");
    6910           1 :             return false;
    6911             :         }
    6912          13 :         const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
    6913          13 :         if (!pszFlagMeanings)
    6914             :         {
    6915           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6916             :                      "Cannot read flag_meanings attribute");
    6917           1 :             return false;
    6918             :         }
    6919             : 
    6920             :         const auto IsSingleDimNumericAttr =
    6921          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
    6922             :         {
    6923          26 :             return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
    6924          26 :                    poAttr->GetDimensionsSize().size() == 1;
    6925             :         };
    6926             : 
    6927          24 :         auto poFlagValues = m_poParent->GetAttribute("flag_values");
    6928             :         const bool bHasFlagValues =
    6929          12 :             poFlagValues && IsSingleDimNumericAttr(poFlagValues);
    6930             : 
    6931          24 :         auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
    6932             :         const bool bHasFlagMasks =
    6933          12 :             poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
    6934             : 
    6935          12 :         if (!bHasFlagValues && !bHasFlagMasks)
    6936             :         {
    6937           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6938             :                      "Cannot find flag_values and/or flag_masks attribute");
    6939           1 :             return false;
    6940             :         }
    6941             : 
    6942             :         const CPLStringList aosUnmaskFlags(
    6943          11 :             CSLTokenizeString2(pszUnmaskFlags, ",", 0));
    6944             :         const CPLStringList aosFlagMeanings(
    6945          11 :             CSLTokenizeString2(pszFlagMeanings, " ", 0));
    6946             : 
    6947          11 :         if (bHasFlagValues)
    6948             :         {
    6949           7 :             const auto eType = poFlagValues->GetDataType().GetNumericDataType();
    6950             :             // We could support Int64 or UInt64, but more work...
    6951           7 :             if (eType != GDT_UInt8 && eType != GDT_Int8 &&
    6952           7 :                 eType != GDT_UInt16 && eType != GDT_Int16 &&
    6953           0 :                 eType != GDT_UInt32 && eType != GDT_Int32)
    6954             :             {
    6955           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    6956             :                          "Unsupported data type for flag_values attribute: %s",
    6957             :                          GDALGetDataTypeName(eType));
    6958           0 :                 return false;
    6959             :             }
    6960             :         }
    6961             : 
    6962          11 :         if (bHasFlagMasks)
    6963             :         {
    6964           6 :             const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
    6965             :             // We could support Int64 or UInt64, but more work...
    6966           6 :             if (eType != GDT_UInt8 && eType != GDT_Int8 &&
    6967           6 :                 eType != GDT_UInt16 && eType != GDT_Int16 &&
    6968           0 :                 eType != GDT_UInt32 && eType != GDT_Int32)
    6969             :             {
    6970           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    6971             :                          "Unsupported data type for flag_masks attribute: %s",
    6972             :                          GDALGetDataTypeName(eType));
    6973           0 :                 return false;
    6974             :             }
    6975             :         }
    6976             : 
    6977             :         const std::vector<double> adfValues(
    6978             :             bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
    6979          11 :                            : std::vector<double>());
    6980             :         const std::vector<double> adfMasks(
    6981             :             bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
    6982          11 :                           : std::vector<double>());
    6983             : 
    6984          18 :         if (bHasFlagValues &&
    6985           7 :             adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
    6986             :         {
    6987           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6988             :                      "Number of values in flag_values attribute is different "
    6989             :                      "from the one in flag_meanings");
    6990           1 :             return false;
    6991             :         }
    6992             : 
    6993          16 :         if (bHasFlagMasks &&
    6994           6 :             adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
    6995             :         {
    6996           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6997             :                      "Number of values in flag_masks attribute is different "
    6998             :                      "from the one in flag_meanings");
    6999           1 :             return false;
    7000             :         }
    7001             : 
    7002          19 :         for (int i = 0; i < aosUnmaskFlags.size(); ++i)
    7003             :         {
    7004          11 :             const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
    7005          11 :             if (nIdxFlag < 0)
    7006             :             {
    7007           1 :                 CPLError(
    7008             :                     CE_Failure, CPLE_AppDefined,
    7009             :                     "Cannot fing flag %s in flag_meanings = '%s' attribute",
    7010             :                     aosUnmaskFlags[i], pszFlagMeanings);
    7011           1 :                 return false;
    7012             :             }
    7013             : 
    7014          10 :             if (bHasFlagValues && adfValues[nIdxFlag] < 0)
    7015             :             {
    7016           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    7017             :                          "Invalid value in flag_values[%d] = %f", nIdxFlag,
    7018           0 :                          adfValues[nIdxFlag]);
    7019           0 :                 return false;
    7020             :             }
    7021             : 
    7022          10 :             if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
    7023             :             {
    7024           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    7025             :                          "Invalid value in flag_masks[%d] = %f", nIdxFlag,
    7026           0 :                          adfMasks[nIdxFlag]);
    7027           0 :                 return false;
    7028             :             }
    7029             : 
    7030          10 :             if (bHasFlagValues)
    7031             :             {
    7032          12 :                 m_anValidFlagValues.push_back(
    7033           6 :                     static_cast<uint32_t>(adfValues[nIdxFlag]));
    7034             :             }
    7035             : 
    7036          10 :             if (bHasFlagMasks)
    7037             :             {
    7038          12 :                 m_anValidFlagMasks.push_back(
    7039           6 :                     static_cast<uint32_t>(adfMasks[nIdxFlag]));
    7040             :             }
    7041             :         }
    7042             :     }
    7043             : 
    7044          42 :     return true;
    7045             : }
    7046             : 
    7047             : /************************************************************************/
    7048             : /*                               IRead()                                */
    7049             : /************************************************************************/
    7050             : 
    7051          51 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    7052             :                             const GInt64 *arrayStep,
    7053             :                             const GPtrDiff_t *bufferStride,
    7054             :                             const GDALExtendedDataType &bufferDataType,
    7055             :                             void *pDstBuffer) const
    7056             : {
    7057          51 :     if (bufferDataType.GetClass() != GEDTC_NUMERIC)
    7058             :     {
    7059           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    7060             :                  "%s: only reading to a numeric data type is supported",
    7061             :                  __func__);
    7062           0 :         return false;
    7063             :     }
    7064          51 :     size_t nElts = 1;
    7065          51 :     const size_t nDims = GetDimensionCount();
    7066         102 :     std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
    7067         139 :     for (size_t i = 0; i < nDims; i++)
    7068          88 :         nElts *= count[i];
    7069          51 :     if (nDims > 0)
    7070             :     {
    7071          46 :         tmpBufferStrideVector.back() = 1;
    7072          88 :         for (size_t i = nDims - 1; i > 0;)
    7073             :         {
    7074          42 :             --i;
    7075          42 :             tmpBufferStrideVector[i] =
    7076          42 :                 tmpBufferStrideVector[i + 1] * count[i + 1];
    7077             :         }
    7078             :     }
    7079             : 
    7080             :     /* Optimized case: if we are an integer data type and that there is no */
    7081             :     /* attribute that can be used to set mask = 0, then fill the mask buffer */
    7082             :     /* directly */
    7083          49 :     if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
    7084          74 :         !m_bHasValidMax && m_anValidFlagValues.empty() &&
    7085          34 :         m_anValidFlagMasks.empty() &&
    7086         111 :         m_poParent->GetRawNoDataValue() == nullptr &&
    7087          11 :         GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
    7088             :     {
    7089           7 :         const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
    7090           7 :         if (bBufferDataTypeIsByte)  // Byte case
    7091             :         {
    7092           4 :             bool bContiguous = true;
    7093          10 :             for (size_t i = 0; i < nDims; i++)
    7094             :             {
    7095           7 :                 if (bufferStride[i] != tmpBufferStrideVector[i])
    7096             :                 {
    7097           1 :                     bContiguous = false;
    7098           1 :                     break;
    7099             :                 }
    7100             :             }
    7101           4 :             if (bContiguous)
    7102             :             {
    7103             :                 // CPLDebug("GDAL", "GetMask(): contiguous case");
    7104           3 :                 memset(pDstBuffer, 1, nElts);
    7105           3 :                 return true;
    7106             :             }
    7107             :         }
    7108             : 
    7109             :         struct Stack
    7110             :         {
    7111             :             size_t nIters = 0;
    7112             :             GByte *dst_ptr = nullptr;
    7113             :             GPtrDiff_t dst_inc_offset = 0;
    7114             :         };
    7115             : 
    7116           4 :         std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
    7117           4 :         const size_t nBufferDTSize = bufferDataType.GetSize();
    7118          13 :         for (size_t i = 0; i < nDims; i++)
    7119             :         {
    7120           9 :             stack[i].dst_inc_offset =
    7121           9 :                 static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    7122             :         }
    7123           4 :         stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    7124             : 
    7125           4 :         size_t dimIdx = 0;
    7126           4 :         const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
    7127             :         GByte abyOne[16];  // 16 is sizeof GDT_CFloat64
    7128           4 :         CPLAssert(nBufferDTSize <= 16);
    7129           4 :         const GByte flag = 1;
    7130           4 :         GDALCopyWords64(&flag, GDT_UInt8, 0, abyOne,
    7131             :                         bufferDataType.GetNumericDataType(), 0, 1);
    7132             : 
    7133          28 :     lbl_next_depth:
    7134          28 :         if (dimIdx == nDimsMinus1)
    7135             :         {
    7136          19 :             auto nIters = nDims > 0 ? count[dimIdx] : 1;
    7137          19 :             GByte *dst_ptr = stack[dimIdx].dst_ptr;
    7138             : 
    7139             :             while (true)
    7140             :             {
    7141             :                 // cppcheck-suppress knownConditionTrueFalse
    7142          73 :                 if (bBufferDataTypeIsByte)
    7143             :                 {
    7144          24 :                     *dst_ptr = flag;
    7145             :                 }
    7146             :                 else
    7147             :                 {
    7148          49 :                     memcpy(dst_ptr, abyOne, nBufferDTSize);
    7149             :                 }
    7150             : 
    7151          73 :                 if ((--nIters) == 0)
    7152          19 :                     break;
    7153          54 :                 dst_ptr += stack[dimIdx].dst_inc_offset;
    7154             :             }
    7155             :         }
    7156             :         else
    7157             :         {
    7158           9 :             stack[dimIdx].nIters = count[dimIdx];
    7159             :             while (true)
    7160             :             {
    7161          24 :                 dimIdx++;
    7162          24 :                 stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    7163          24 :                 goto lbl_next_depth;
    7164          24 :             lbl_return_to_caller:
    7165          24 :                 dimIdx--;
    7166          24 :                 if ((--stack[dimIdx].nIters) == 0)
    7167           9 :                     break;
    7168          15 :                 stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    7169             :             }
    7170             :         }
    7171          28 :         if (dimIdx > 0)
    7172          24 :             goto lbl_return_to_caller;
    7173             : 
    7174           4 :         return true;
    7175             :     }
    7176             : 
    7177             :     const auto oTmpBufferDT =
    7178          44 :         GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
    7179             :             ? GDALExtendedDataType::Create(GDT_Float64)
    7180          88 :             : m_poParent->GetDataType();
    7181          44 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
    7182          44 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
    7183          44 :     if (!pTempBuffer)
    7184           0 :         return false;
    7185          88 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
    7186          44 :                           tmpBufferStrideVector.data(), oTmpBufferDT,
    7187             :                           pTempBuffer))
    7188             :     {
    7189           0 :         VSIFree(pTempBuffer);
    7190           0 :         return false;
    7191             :     }
    7192             : 
    7193          44 :     switch (oTmpBufferDT.GetNumericDataType())
    7194             :     {
    7195           7 :         case GDT_UInt8:
    7196           7 :             ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
    7197             :                                 pTempBuffer, oTmpBufferDT,
    7198             :                                 tmpBufferStrideVector);
    7199           7 :             break;
    7200             : 
    7201           0 :         case GDT_Int8:
    7202           0 :             ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
    7203             :                                 pTempBuffer, oTmpBufferDT,
    7204             :                                 tmpBufferStrideVector);
    7205           0 :             break;
    7206             : 
    7207           1 :         case GDT_UInt16:
    7208           1 :             ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
    7209             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
    7210             :                                   tmpBufferStrideVector);
    7211           1 :             break;
    7212             : 
    7213          14 :         case GDT_Int16:
    7214          14 :             ReadInternal<GInt16>(count, bufferStride, bufferDataType,
    7215             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7216             :                                  tmpBufferStrideVector);
    7217          14 :             break;
    7218             : 
    7219           1 :         case GDT_UInt32:
    7220           1 :             ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
    7221             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
    7222             :                                   tmpBufferStrideVector);
    7223           1 :             break;
    7224             : 
    7225           5 :         case GDT_Int32:
    7226           5 :             ReadInternal<GInt32>(count, bufferStride, bufferDataType,
    7227             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7228             :                                  tmpBufferStrideVector);
    7229           5 :             break;
    7230             : 
    7231           0 :         case GDT_UInt64:
    7232           0 :             ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
    7233             :                                         pDstBuffer, pTempBuffer, oTmpBufferDT,
    7234             :                                         tmpBufferStrideVector);
    7235           0 :             break;
    7236             : 
    7237           0 :         case GDT_Int64:
    7238           0 :             ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
    7239             :                                        pDstBuffer, pTempBuffer, oTmpBufferDT,
    7240             :                                        tmpBufferStrideVector);
    7241           0 :             break;
    7242             : 
    7243           0 :         case GDT_Float16:
    7244           0 :             ReadInternal<GFloat16>(count, bufferStride, bufferDataType,
    7245             :                                    pDstBuffer, pTempBuffer, oTmpBufferDT,
    7246             :                                    tmpBufferStrideVector);
    7247           0 :             break;
    7248             : 
    7249           7 :         case GDT_Float32:
    7250           7 :             ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
    7251             :                                 pTempBuffer, oTmpBufferDT,
    7252             :                                 tmpBufferStrideVector);
    7253           7 :             break;
    7254             : 
    7255           9 :         case GDT_Float64:
    7256           9 :             ReadInternal<double>(count, bufferStride, bufferDataType,
    7257             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7258             :                                  tmpBufferStrideVector);
    7259           9 :             break;
    7260           0 :         case GDT_Unknown:
    7261             :         case GDT_CInt16:
    7262             :         case GDT_CInt32:
    7263             :         case GDT_CFloat16:
    7264             :         case GDT_CFloat32:
    7265             :         case GDT_CFloat64:
    7266             :         case GDT_TypeCount:
    7267           0 :             CPLAssert(false);
    7268             :             break;
    7269             :     }
    7270             : 
    7271          44 :     VSIFree(pTempBuffer);
    7272             : 
    7273          44 :     return true;
    7274             : }
    7275             : 
    7276             : /************************************************************************/
    7277             : /*                            IsValidForDT()                            */
    7278             : /************************************************************************/
    7279             : 
    7280          40 : template <typename Type> static bool IsValidForDT(double dfVal)
    7281             : {
    7282          40 :     if (std::isnan(dfVal))
    7283           0 :         return false;
    7284          40 :     if (dfVal < static_cast<double>(cpl::NumericLimits<Type>::lowest()))
    7285           0 :         return false;
    7286          40 :     if (dfVal > static_cast<double>(cpl::NumericLimits<Type>::max()))
    7287           0 :         return false;
    7288          40 :     return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
    7289             : }
    7290             : 
    7291           9 : template <> bool IsValidForDT<double>(double)
    7292             : {
    7293           9 :     return true;
    7294             : }
    7295             : 
    7296             : /************************************************************************/
    7297             : /*                               IsNan()                                */
    7298             : /************************************************************************/
    7299             : 
    7300        1438 : template <typename Type> inline bool IsNan(Type)
    7301             : {
    7302        1438 :     return false;
    7303             : }
    7304             : 
    7305          65 : template <> bool IsNan<double>(double val)
    7306             : {
    7307          65 :     return std::isnan(val);
    7308             : }
    7309             : 
    7310          26 : template <> bool IsNan<float>(float val)
    7311             : {
    7312          26 :     return std::isnan(val);
    7313             : }
    7314             : 
    7315             : /************************************************************************/
    7316             : /*                            ReadInternal()                            */
    7317             : /************************************************************************/
    7318             : 
    7319             : template <typename Type>
    7320          44 : void GDALMDArrayMask::ReadInternal(
    7321             :     const size_t *count, const GPtrDiff_t *bufferStride,
    7322             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    7323             :     const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
    7324             :     const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
    7325             : {
    7326          44 :     const size_t nDims = GetDimensionCount();
    7327             : 
    7328         220 :     const auto castValue = [](bool &bHasVal, double dfVal) -> Type
    7329             :     {
    7330         220 :         if (bHasVal)
    7331             :         {
    7332          49 :             if (IsValidForDT<Type>(dfVal))
    7333             :             {
    7334          49 :                 return static_cast<Type>(dfVal);
    7335             :             }
    7336             :             else
    7337             :             {
    7338           0 :                 bHasVal = false;
    7339             :             }
    7340             :         }
    7341         171 :         return 0;
    7342             :     };
    7343             : 
    7344          44 :     const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
    7345          44 :     bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
    7346             :     const Type nNoDataValue =
    7347          44 :         castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
    7348          44 :     bool bHasMissingValue = m_bHasMissingValue;
    7349          44 :     const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
    7350          44 :     bool bHasFillValue = m_bHasFillValue;
    7351          44 :     const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
    7352          44 :     bool bHasValidMin = m_bHasValidMin;
    7353          44 :     const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
    7354          44 :     bool bHasValidMax = m_bHasValidMax;
    7355          44 :     const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
    7356          44 :     const bool bHasValidFlags =
    7357          44 :         !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
    7358             : 
    7359         351 :     const auto IsValidFlag = [this](Type v)
    7360             :     {
    7361          54 :         if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
    7362             :         {
    7363          20 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
    7364             :             {
    7365          12 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
    7366             :                     m_anValidFlagValues[i])
    7367             :                 {
    7368           4 :                     return true;
    7369             :                 }
    7370             :             }
    7371             :         }
    7372          42 :         else if (!m_anValidFlagValues.empty())
    7373             :         {
    7374          49 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
    7375             :             {
    7376          29 :                 if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
    7377             :                 {
    7378           4 :                     return true;
    7379             :                 }
    7380             :             }
    7381             :         }
    7382             :         else /* if( !m_anValidFlagMasks.empty() ) */
    7383             :         {
    7384          31 :             for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
    7385             :             {
    7386          22 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
    7387             :                 {
    7388           9 :                     return true;
    7389             :                 }
    7390             :             }
    7391             :         }
    7392          37 :         return false;
    7393             :     };
    7394             : 
    7395             : #define GET_MASK_FOR_SAMPLE(v)                                                 \
    7396             :     static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
    7397             :                        !(bHasMissingValue && v == nMissingValue) &&            \
    7398             :                        !(bHasFillValue && v == nFillValue) &&                  \
    7399             :                        !(bHasValidMin && v < nValidMin) &&                     \
    7400             :                        !(bHasValidMax && v > nValidMax) &&                     \
    7401             :                        (!bHasValidFlags || IsValidFlag(v)));
    7402             : 
    7403          44 :     const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
    7404             :     /* Optimized case: Byte output and output buffer is contiguous */
    7405          44 :     if (bBufferDataTypeIsByte)
    7406             :     {
    7407          40 :         bool bContiguous = true;
    7408         103 :         for (size_t i = 0; i < nDims; i++)
    7409             :         {
    7410          64 :             if (bufferStride[i] != tmpBufferStrideVector[i])
    7411             :             {
    7412           1 :                 bContiguous = false;
    7413           1 :                 break;
    7414             :             }
    7415             :         }
    7416          40 :         if (bContiguous)
    7417             :         {
    7418          39 :             size_t nElts = 1;
    7419         102 :             for (size_t i = 0; i < nDims; i++)
    7420          63 :                 nElts *= count[i];
    7421             : 
    7422        1113 :             for (size_t i = 0; i < nElts; i++)
    7423             :             {
    7424        1074 :                 const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
    7425        1074 :                 static_cast<GByte *>(pDstBuffer)[i] =
    7426        1074 :                     GET_MASK_FOR_SAMPLE(*pSrc);
    7427             :             }
    7428          39 :             return;
    7429             :         }
    7430             :     }
    7431             : 
    7432           5 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
    7433             : 
    7434             :     struct Stack
    7435             :     {
    7436             :         size_t nIters = 0;
    7437             :         const GByte *src_ptr = nullptr;
    7438             :         GByte *dst_ptr = nullptr;
    7439             :         GPtrDiff_t src_inc_offset = 0;
    7440             :         GPtrDiff_t dst_inc_offset = 0;
    7441             :     };
    7442             : 
    7443          10 :     std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
    7444           5 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    7445          15 :     for (size_t i = 0; i < nDims; i++)
    7446             :     {
    7447          20 :         stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
    7448          10 :             tmpBufferStrideVector[i] * nTmpBufferDTSize);
    7449          10 :         stack[i].dst_inc_offset =
    7450          10 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    7451             :     }
    7452           5 :     stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
    7453           5 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    7454             : 
    7455           5 :     size_t dimIdx = 0;
    7456           5 :     const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
    7457             :     GByte abyZeroOrOne[2][16];  // 16 is sizeof GDT_CFloat64
    7458           5 :     CPLAssert(nBufferDTSize <= 16);
    7459          15 :     for (GByte flag = 0; flag <= 1; flag++)
    7460             :     {
    7461          10 :         GDALCopyWords64(&flag, m_dt.GetNumericDataType(), 0, abyZeroOrOne[flag],
    7462             :                         bufferDataType.GetNumericDataType(), 0, 1);
    7463             :     }
    7464             : 
    7465          43 : lbl_next_depth:
    7466          43 :     if (dimIdx == nDimsMinus1)
    7467             :     {
    7468          35 :         auto nIters = nDims > 0 ? count[dimIdx] : 1;
    7469          35 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
    7470          35 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
    7471             : 
    7472         420 :         while (true)
    7473             :         {
    7474         455 :             const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
    7475         455 :             const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
    7476             : 
    7477         455 :             if (bBufferDataTypeIsByte)
    7478             :             {
    7479          24 :                 *dst_ptr = flag;
    7480             :             }
    7481             :             else
    7482             :             {
    7483         431 :                 memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
    7484             :             }
    7485             : 
    7486         455 :             if ((--nIters) == 0)
    7487          35 :                 break;
    7488         420 :             src_ptr += stack[dimIdx].src_inc_offset;
    7489         420 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    7490             :         }
    7491             :     }
    7492             :     else
    7493             :     {
    7494           8 :         stack[dimIdx].nIters = count[dimIdx];
    7495             :         while (true)
    7496             :         {
    7497          38 :             dimIdx++;
    7498          38 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    7499          38 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    7500          38 :             goto lbl_next_depth;
    7501          38 :         lbl_return_to_caller:
    7502          38 :             dimIdx--;
    7503          38 :             if ((--stack[dimIdx].nIters) == 0)
    7504           8 :                 break;
    7505          30 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    7506          30 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    7507             :         }
    7508             :     }
    7509          43 :     if (dimIdx > 0)
    7510          38 :         goto lbl_return_to_caller;
    7511             : }
    7512             : 
    7513             : /************************************************************************/
    7514             : /*                              GetMask()                               */
    7515             : /************************************************************************/
    7516             : 
    7517             : /** Return an array that is a mask for the current array
    7518             : 
    7519             :  This array will be of type Byte, with values set to 0 to indicate invalid
    7520             :  pixels of the current array, and values set to 1 to indicate valid pixels.
    7521             : 
    7522             :  The generic implementation honours the NoDataValue, as well as various
    7523             :  netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
    7524             :  and valid_range.
    7525             : 
    7526             :  Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
    7527             :  can be used to specify strings of the "flag_meanings" attribute
    7528             :  (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
    7529             :  for which pixels matching any of those flags will be set at 1 in the mask array,
    7530             :  and pixels matching none of those flags will be set at 0.
    7531             :  For example, let's consider the following netCDF variable defined with:
    7532             :  \verbatim
    7533             :  l2p_flags:valid_min = 0s ;
    7534             :  l2p_flags:valid_max = 256s ;
    7535             :  l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
    7536             :  l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
    7537             :  \endverbatim
    7538             : 
    7539             :  GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
    7540             :  - for pixel values *outside* valid_range [0,256], the mask value will be 0.
    7541             :  - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
    7542             :    will be 1.
    7543             :  - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
    7544             :    will be 0.
    7545             : 
    7546             :  This is the same as the C function GDALMDArrayGetMask().
    7547             : 
    7548             :  @param papszOptions NULL-terminated list of options, or NULL.
    7549             : 
    7550             :  @return a new array, that holds a reference to the original one, and thus is
    7551             :  a view of it (not a copy), or nullptr in case of error.
    7552             : */
    7553             : std::shared_ptr<GDALMDArray>
    7554          49 : GDALMDArray::GetMask(CSLConstList papszOptions) const
    7555             : {
    7556          98 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    7557          49 :     if (!self)
    7558             :     {
    7559           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    7560             :                  "Driver implementation issue: m_pSelf not set !");
    7561           0 :         return nullptr;
    7562             :     }
    7563          49 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    7564             :     {
    7565           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    7566             :                  "GetMask() only supports numeric data type");
    7567           1 :         return nullptr;
    7568             :     }
    7569          48 :     return GDALMDArrayMask::Create(self, papszOptions);
    7570             : }
    7571             : 
    7572             : /************************************************************************/
    7573             : /*                         IsRegularlySpaced()                          */
    7574             : /************************************************************************/
    7575             : 
    7576             : /** Returns whether an array is a 1D regularly spaced array.
    7577             :  *
    7578             :  * @param[out] dfStart     First value in the array
    7579             :  * @param[out] dfIncrement Increment/spacing between consecutive values.
    7580             :  * @return true if the array is regularly spaced.
    7581             :  */
    7582         355 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
    7583             : {
    7584         355 :     dfStart = 0;
    7585         355 :     dfIncrement = 0;
    7586         355 :     if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
    7587           0 :         return false;
    7588         355 :     const auto nSize = GetDimensions()[0]->GetSize();
    7589         355 :     if (nSize <= 1 || nSize > 10 * 1000 * 1000)
    7590           2 :         return false;
    7591             : 
    7592         353 :     size_t nCount = static_cast<size_t>(nSize);
    7593         706 :     std::vector<double> adfTmp;
    7594             :     try
    7595             :     {
    7596         353 :         adfTmp.resize(nCount);
    7597             :     }
    7598           0 :     catch (const std::exception &)
    7599             :     {
    7600           0 :         return false;
    7601             :     }
    7602             : 
    7603         353 :     GUInt64 anStart[1] = {0};
    7604         353 :     size_t anCount[1] = {nCount};
    7605             : 
    7606             :     const auto IsRegularlySpacedInternal =
    7607       45445 :         [&dfStart, &dfIncrement, &anCount, &adfTmp]()
    7608             :     {
    7609         406 :         dfStart = adfTmp[0];
    7610         406 :         dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
    7611         406 :         if (dfIncrement == 0)
    7612             :         {
    7613           3 :             return false;
    7614             :         }
    7615       11247 :         for (size_t i = 1; i < anCount[0]; i++)
    7616             :         {
    7617       10858 :             if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
    7618       10858 :                 1e-3 * fabs(dfIncrement))
    7619             :             {
    7620          14 :                 return false;
    7621             :             }
    7622             :         }
    7623         389 :         return true;
    7624         353 :     };
    7625             : 
    7626             :     // First try with the first block(s). This can avoid excessive processing
    7627             :     // time, for example with Zarr datasets.
    7628             :     // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
    7629             :     // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
    7630         353 :     const auto nBlockSize = GetBlockSize()[0];
    7631         353 :     if (nCount >= 5 && nBlockSize <= nCount / 2)
    7632             :     {
    7633             :         size_t nReducedCount =
    7634          56 :             std::max<size_t>(3, static_cast<size_t>(nBlockSize));
    7635         176 :         while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
    7636         120 :             nReducedCount *= 2;
    7637          56 :         anCount[0] = nReducedCount;
    7638          56 :         if (!Read(anStart, anCount, nullptr, nullptr,
    7639         112 :                   GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
    7640             :         {
    7641           0 :             return false;
    7642             :         }
    7643          56 :         if (!IsRegularlySpacedInternal())
    7644             :         {
    7645           3 :             return false;
    7646             :         }
    7647             : 
    7648             :         // Get next values
    7649          53 :         anStart[0] = nReducedCount;
    7650          53 :         anCount[0] = nCount - nReducedCount;
    7651             :     }
    7652             : 
    7653         350 :     if (!Read(anStart, anCount, nullptr, nullptr,
    7654         700 :               GDALExtendedDataType::Create(GDT_Float64),
    7655         350 :               &adfTmp[static_cast<size_t>(anStart[0])]))
    7656             :     {
    7657           0 :         return false;
    7658             :     }
    7659             : 
    7660         350 :     return IsRegularlySpacedInternal();
    7661             : }
    7662             : 
    7663             : /************************************************************************/
    7664             : /*                         GuessGeoTransform()                          */
    7665             : /************************************************************************/
    7666             : 
    7667             : /** Returns whether 2 specified dimensions form a geotransform
    7668             :  *
    7669             :  * @param nDimX                Index of the X axis.
    7670             :  * @param nDimY                Index of the Y axis.
    7671             :  * @param bPixelIsPoint        Whether the geotransform should be returned
    7672             :  *                             with the pixel-is-point (pixel-center) convention
    7673             :  *                             (bPixelIsPoint = true), or with the pixel-is-area
    7674             :  *                             (top left corner convention)
    7675             :  *                             (bPixelIsPoint = false)
    7676             :  * @param[out] gt              Computed geotransform
    7677             :  * @return true if a geotransform could be computed.
    7678             :  */
    7679         498 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
    7680             :                                     bool bPixelIsPoint,
    7681             :                                     GDALGeoTransform &gt) const
    7682             : {
    7683         498 :     const auto &dims(GetDimensions());
    7684         996 :     auto poVarX = dims[nDimX]->GetIndexingVariable();
    7685         996 :     auto poVarY = dims[nDimY]->GetIndexingVariable();
    7686         498 :     double dfXStart = 0.0;
    7687         498 :     double dfXSpacing = 0.0;
    7688         498 :     double dfYStart = 0.0;
    7689         498 :     double dfYSpacing = 0.0;
    7690        1112 :     if (poVarX && poVarX->GetDimensionCount() == 1 &&
    7691         614 :         poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
    7692         897 :         poVarY && poVarY->GetDimensionCount() == 1 &&
    7693         295 :         poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
    7694        1095 :         poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
    7695         290 :         poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
    7696             :     {
    7697         288 :         gt.xorig = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
    7698         288 :         gt.xscale = dfXSpacing;
    7699         288 :         gt.xrot = 0;
    7700         288 :         gt.yorig = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
    7701         288 :         gt.yrot = 0;
    7702         288 :         gt.yscale = dfYSpacing;
    7703         288 :         return true;
    7704             :     }
    7705         210 :     return false;
    7706             : }
    7707             : 
    7708             : /** Returns whether 2 specified dimensions form a geotransform
    7709             :  *
    7710             :  * @param nDimX                Index of the X axis.
    7711             :  * @param nDimY                Index of the Y axis.
    7712             :  * @param bPixelIsPoint        Whether the geotransform should be returned
    7713             :  *                             with the pixel-is-point (pixel-center) convention
    7714             :  *                             (bPixelIsPoint = true), or with the pixel-is-area
    7715             :  *                             (top left corner convention)
    7716             :  *                             (bPixelIsPoint = false)
    7717             :  * @param[out] adfGeoTransform Computed geotransform
    7718             :  * @return true if a geotransform could be computed.
    7719             :  */
    7720           0 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
    7721             :                                     bool bPixelIsPoint,
    7722             :                                     double adfGeoTransform[6]) const
    7723             : {
    7724           0 :     GDALGeoTransform *gt =
    7725             :         reinterpret_cast<GDALGeoTransform *>(adfGeoTransform);
    7726           0 :     return GuessGeoTransform(nDimX, nDimY, bPixelIsPoint, *gt);
    7727             : }
    7728             : 
    7729             : /************************************************************************/
    7730             : /*                         GDALMDArrayResampled                         */
    7731             : /************************************************************************/
    7732             : 
    7733             : class GDALMDArrayResampledDataset;
    7734             : 
    7735             : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
    7736             : {
    7737             :   protected:
    7738             :     CPLErr IReadBlock(int, int, void *) override;
    7739             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
    7740             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
    7741             :                      GDALDataType eBufType, GSpacing nPixelSpaceBuf,
    7742             :                      GSpacing nLineSpaceBuf,
    7743             :                      GDALRasterIOExtraArg *psExtraArg) override;
    7744             : 
    7745             :   public:
    7746             :     explicit GDALMDArrayResampledDatasetRasterBand(
    7747             :         GDALMDArrayResampledDataset *poDSIn);
    7748             : 
    7749             :     double GetNoDataValue(int *pbHasNoData) override;
    7750             : };
    7751             : 
    7752             : class GDALMDArrayResampledDataset final : public GDALPamDataset
    7753             : {
    7754             :     friend class GDALMDArrayResampled;
    7755             :     friend class GDALMDArrayResampledDatasetRasterBand;
    7756             : 
    7757             :     std::shared_ptr<GDALMDArray> m_poArray;
    7758             :     const size_t m_iXDim;
    7759             :     const size_t m_iYDim;
    7760             :     GDALGeoTransform m_gt{};
    7761             :     bool m_bHasGT = false;
    7762             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
    7763             : 
    7764             :     std::vector<GUInt64> m_anOffset{};
    7765             :     std::vector<size_t> m_anCount{};
    7766             :     std::vector<GPtrDiff_t> m_anStride{};
    7767             : 
    7768             :     std::string m_osFilenameLong{};
    7769             :     std::string m_osFilenameLat{};
    7770             : 
    7771             :   public:
    7772          24 :     GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
    7773             :                                 size_t iXDim, size_t iYDim)
    7774          24 :         : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
    7775          24 :           m_anOffset(m_poArray->GetDimensionCount(), 0),
    7776          24 :           m_anCount(m_poArray->GetDimensionCount(), 1),
    7777          72 :           m_anStride(m_poArray->GetDimensionCount(), 0)
    7778             :     {
    7779          24 :         const auto &dims(m_poArray->GetDimensions());
    7780             : 
    7781          24 :         nRasterYSize = static_cast<int>(
    7782          24 :             std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
    7783          24 :         nRasterXSize = static_cast<int>(
    7784          24 :             std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
    7785             : 
    7786          24 :         m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false, m_gt);
    7787             : 
    7788          24 :         SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
    7789          24 :     }
    7790             : 
    7791             :     ~GDALMDArrayResampledDataset() override;
    7792             : 
    7793          43 :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override
    7794             :     {
    7795          43 :         gt = m_gt;
    7796          43 :         return m_bHasGT ? CE_None : CE_Failure;
    7797             :     }
    7798             : 
    7799         105 :     const OGRSpatialReference *GetSpatialRef() const override
    7800             :     {
    7801         105 :         m_poSRS = m_poArray->GetSpatialRef();
    7802         105 :         if (m_poSRS)
    7803             :         {
    7804          79 :             m_poSRS.reset(m_poSRS->Clone());
    7805         158 :             auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
    7806         237 :             for (auto &m : axisMapping)
    7807             :             {
    7808         158 :                 if (m == static_cast<int>(m_iXDim) + 1)
    7809          79 :                     m = 1;
    7810          79 :                 else if (m == static_cast<int>(m_iYDim) + 1)
    7811          79 :                     m = 2;
    7812             :             }
    7813          79 :             m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
    7814             :         }
    7815         105 :         return m_poSRS.get();
    7816             :     }
    7817             : 
    7818           5 :     void SetGeolocationArray(const std::string &osFilenameLong,
    7819             :                              const std::string &osFilenameLat)
    7820             :     {
    7821           5 :         m_osFilenameLong = osFilenameLong;
    7822           5 :         m_osFilenameLat = osFilenameLat;
    7823          10 :         CPLStringList aosGeoLoc;
    7824           5 :         aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
    7825           5 :         aosGeoLoc.SetNameValue("LINE_STEP", "1");
    7826           5 :         aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
    7827           5 :         aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
    7828           5 :         aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG);  // FIXME?
    7829           5 :         aosGeoLoc.SetNameValue("X_BAND", "1");
    7830           5 :         aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
    7831           5 :         aosGeoLoc.SetNameValue("Y_BAND", "1");
    7832           5 :         aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
    7833           5 :         aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
    7834           5 :         SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
    7835           5 :     }
    7836             : };
    7837             : 
    7838          48 : GDALMDArrayResampledDataset::~GDALMDArrayResampledDataset()
    7839             : {
    7840          24 :     if (!m_osFilenameLong.empty())
    7841           5 :         VSIUnlink(m_osFilenameLong.c_str());
    7842          24 :     if (!m_osFilenameLat.empty())
    7843           5 :         VSIUnlink(m_osFilenameLat.c_str());
    7844          48 : }
    7845             : 
    7846             : /************************************************************************/
    7847             : /*               GDALMDArrayResampledDatasetRasterBand()                */
    7848             : /************************************************************************/
    7849             : 
    7850          24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
    7851          24 :     GDALMDArrayResampledDataset *poDSIn)
    7852             : {
    7853          24 :     const auto &poArray(poDSIn->m_poArray);
    7854          24 :     const auto blockSize(poArray->GetBlockSize());
    7855          24 :     nBlockYSize = (blockSize[poDSIn->m_iYDim])
    7856          24 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    7857          13 :                                                   blockSize[poDSIn->m_iYDim]))
    7858          24 :                       : 1;
    7859          24 :     nBlockXSize = blockSize[poDSIn->m_iXDim]
    7860          13 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    7861          13 :                                                   blockSize[poDSIn->m_iXDim]))
    7862          24 :                       : poDSIn->GetRasterXSize();
    7863          24 :     eDataType = poArray->GetDataType().GetNumericDataType();
    7864          24 :     eAccess = poDSIn->eAccess;
    7865          24 : }
    7866             : 
    7867             : /************************************************************************/
    7868             : /*                           GetNoDataValue()                           */
    7869             : /************************************************************************/
    7870             : 
    7871          54 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
    7872             : {
    7873          54 :     auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
    7874          54 :     const auto &poArray(l_poDS->m_poArray);
    7875          54 :     bool bHasNodata = false;
    7876          54 :     double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
    7877          54 :     if (pbHasNoData)
    7878          48 :         *pbHasNoData = bHasNodata;
    7879          54 :     return dfRes;
    7880             : }
    7881             : 
    7882             : /************************************************************************/
    7883             : /*                             IReadBlock()                             */
    7884             : /************************************************************************/
    7885             : 
    7886           0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
    7887             :                                                          int nBlockYOff,
    7888             :                                                          void *pImage)
    7889             : {
    7890           0 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    7891           0 :     const int nXOff = nBlockXOff * nBlockXSize;
    7892           0 :     const int nYOff = nBlockYOff * nBlockYSize;
    7893           0 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    7894           0 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    7895             :     GDALRasterIOExtraArg sExtraArg;
    7896           0 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    7897           0 :     return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    7898             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    7899           0 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    7900             : }
    7901             : 
    7902             : /************************************************************************/
    7903             : /*                             IRasterIO()                              */
    7904             : /************************************************************************/
    7905             : 
    7906          32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
    7907             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
    7908             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
    7909             :     GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
    7910             :     GDALRasterIOExtraArg *psExtraArg)
    7911             : {
    7912          32 :     auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
    7913          32 :     const auto &poArray(l_poDS->m_poArray);
    7914          32 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
    7915          32 :     if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
    7916          32 :         nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
    7917          32 :         (nLineSpaceBuf % nBufferDTSize) == 0)
    7918             :     {
    7919          32 :         l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
    7920          32 :         l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
    7921          64 :         l_poDS->m_anStride[l_poDS->m_iXDim] =
    7922          32 :             static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
    7923             : 
    7924          32 :         l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
    7925          32 :         l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
    7926          64 :         l_poDS->m_anStride[l_poDS->m_iYDim] =
    7927          32 :             static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
    7928             : 
    7929          64 :         return poArray->Read(l_poDS->m_anOffset.data(),
    7930          32 :                              l_poDS->m_anCount.data(), nullptr,
    7931          32 :                              l_poDS->m_anStride.data(),
    7932          64 :                              GDALExtendedDataType::Create(eBufType), pData)
    7933          32 :                    ? CE_None
    7934          32 :                    : CE_Failure;
    7935             :     }
    7936           0 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    7937             :                                      pData, nBufXSize, nBufYSize, eBufType,
    7938           0 :                                      nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
    7939             : }
    7940             : 
    7941             : class GDALMDArrayResampled final : public GDALPamMDArray
    7942             : {
    7943             :   private:
    7944             :     std::shared_ptr<GDALMDArray> m_poParent{};
    7945             :     std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
    7946             :     std::vector<GUInt64> m_anBlockSize;
    7947             :     GDALExtendedDataType m_dt;
    7948             :     std::shared_ptr<OGRSpatialReference> m_poSRS{};
    7949             :     std::shared_ptr<GDALMDArray> m_poVarX{};
    7950             :     std::shared_ptr<GDALMDArray> m_poVarY{};
    7951             :     std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
    7952             :     std::unique_ptr<GDALDataset> m_poReprojectedDS{};
    7953             : 
    7954             :   protected:
    7955          21 :     GDALMDArrayResampled(
    7956             :         const std::shared_ptr<GDALMDArray> &poParent,
    7957             :         const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
    7958             :         const std::vector<GUInt64> &anBlockSize)
    7959          42 :         : GDALAbstractMDArray(std::string(),
    7960          42 :                               "Resampled view of " + poParent->GetFullName()),
    7961             :           GDALPamMDArray(
    7962          42 :               std::string(), "Resampled view of " + poParent->GetFullName(),
    7963          42 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
    7964          21 :           m_poParent(std::move(poParent)), m_apoDims(apoDims),
    7965         105 :           m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
    7966             :     {
    7967          21 :         CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
    7968          21 :         CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
    7969          21 :     }
    7970             : 
    7971             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    7972             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    7973             :                const GDALExtendedDataType &bufferDataType,
    7974             :                void *pDstBuffer) const override;
    7975             : 
    7976             :   public:
    7977             :     static std::shared_ptr<GDALMDArray>
    7978             :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    7979             :            const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
    7980             :            GDALRIOResampleAlg resampleAlg,
    7981             :            const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
    7982             : 
    7983          42 :     ~GDALMDArrayResampled() override
    7984          21 :     {
    7985             :         // First close the warped VRT
    7986          21 :         m_poReprojectedDS.reset();
    7987          21 :         m_poParentDS.reset();
    7988          42 :     }
    7989             : 
    7990          11 :     bool IsWritable() const override
    7991             :     {
    7992          11 :         return false;
    7993             :     }
    7994             : 
    7995          74 :     const std::string &GetFilename() const override
    7996             :     {
    7997          74 :         return m_poParent->GetFilename();
    7998             :     }
    7999             : 
    8000             :     const std::vector<std::shared_ptr<GDALDimension>> &
    8001         257 :     GetDimensions() const override
    8002             :     {
    8003         257 :         return m_apoDims;
    8004             :     }
    8005             : 
    8006         109 :     const GDALExtendedDataType &GetDataType() const override
    8007             :     {
    8008         109 :         return m_dt;
    8009             :     }
    8010             : 
    8011          21 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    8012             :     {
    8013          21 :         return m_poSRS;
    8014             :     }
    8015             : 
    8016          12 :     std::vector<GUInt64> GetBlockSize() const override
    8017             :     {
    8018          12 :         return m_anBlockSize;
    8019             :     }
    8020             : 
    8021             :     std::shared_ptr<GDALAttribute>
    8022           1 :     GetAttribute(const std::string &osName) const override
    8023             :     {
    8024           1 :         return m_poParent->GetAttribute(osName);
    8025             :     }
    8026             : 
    8027             :     std::vector<std::shared_ptr<GDALAttribute>>
    8028          12 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    8029             :     {
    8030          12 :         return m_poParent->GetAttributes(papszOptions);
    8031             :     }
    8032             : 
    8033           1 :     const std::string &GetUnit() const override
    8034             :     {
    8035           1 :         return m_poParent->GetUnit();
    8036             :     }
    8037             : 
    8038           1 :     const void *GetRawNoDataValue() const override
    8039             :     {
    8040           1 :         return m_poParent->GetRawNoDataValue();
    8041             :     }
    8042             : 
    8043           1 :     double GetOffset(bool *pbHasOffset,
    8044             :                      GDALDataType *peStorageType) const override
    8045             :     {
    8046           1 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    8047             :     }
    8048             : 
    8049           1 :     double GetScale(bool *pbHasScale,
    8050             :                     GDALDataType *peStorageType) const override
    8051             :     {
    8052           1 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    8053             :     }
    8054             : };
    8055             : 
    8056             : /************************************************************************/
    8057             : /*                    GDALMDArrayResampled::Create()                    */
    8058             : /************************************************************************/
    8059             : 
    8060          29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
    8061             :     const std::shared_ptr<GDALMDArray> &poParent,
    8062             :     const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
    8063             :     GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
    8064             :     CSLConstList /* papszOptions */)
    8065             : {
    8066          29 :     const char *pszResampleAlg = "nearest";
    8067          29 :     bool unsupported = false;
    8068          29 :     switch (resampleAlg)
    8069             :     {
    8070          16 :         case GRIORA_NearestNeighbour:
    8071          16 :             pszResampleAlg = "nearest";
    8072          16 :             break;
    8073           2 :         case GRIORA_Bilinear:
    8074           2 :             pszResampleAlg = "bilinear";
    8075           2 :             break;
    8076           5 :         case GRIORA_Cubic:
    8077           5 :             pszResampleAlg = "cubic";
    8078           5 :             break;
    8079           1 :         case GRIORA_CubicSpline:
    8080           1 :             pszResampleAlg = "cubicspline";
    8081           1 :             break;
    8082           1 :         case GRIORA_Lanczos:
    8083           1 :             pszResampleAlg = "lanczos";
    8084           1 :             break;
    8085           1 :         case GRIORA_Average:
    8086           1 :             pszResampleAlg = "average";
    8087           1 :             break;
    8088           1 :         case GRIORA_Mode:
    8089           1 :             pszResampleAlg = "mode";
    8090           1 :             break;
    8091           1 :         case GRIORA_Gauss:
    8092           1 :             unsupported = true;
    8093           1 :             break;
    8094           0 :         case GRIORA_RESERVED_START:
    8095           0 :             unsupported = true;
    8096           0 :             break;
    8097           0 :         case GRIORA_RESERVED_END:
    8098           0 :             unsupported = true;
    8099           0 :             break;
    8100           1 :         case GRIORA_RMS:
    8101           1 :             pszResampleAlg = "rms";
    8102           1 :             break;
    8103             :     }
    8104          29 :     if (unsupported)
    8105             :     {
    8106           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    8107             :                  "Unsupported resample method for GetResampled()");
    8108           1 :         return nullptr;
    8109             :     }
    8110             : 
    8111          28 :     if (poParent->GetDimensionCount() < 2)
    8112             :     {
    8113           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    8114             :                  "GetResampled() only supports 2 dimensions or more");
    8115           1 :         return nullptr;
    8116             :     }
    8117             : 
    8118          27 :     const auto &aoParentDims = poParent->GetDimensions();
    8119          27 :     if (apoNewDimsIn.size() != aoParentDims.size())
    8120             :     {
    8121           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8122             :                  "GetResampled(): apoNewDims size should be the same as "
    8123             :                  "GetDimensionCount()");
    8124           2 :         return nullptr;
    8125             :     }
    8126             : 
    8127          50 :     std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
    8128          25 :     apoNewDims.reserve(apoNewDimsIn.size());
    8129             : 
    8130          50 :     std::vector<GUInt64> anBlockSize;
    8131          25 :     anBlockSize.reserve(apoNewDimsIn.size());
    8132          50 :     const auto &anParentBlockSize = poParent->GetBlockSize();
    8133             : 
    8134          50 :     auto apoParentDims = poParent->GetDimensions();
    8135             :     // Special case for NASA EMIT datasets
    8136          30 :     const bool bYXBandOrder = (apoParentDims.size() == 3 &&
    8137           7 :                                apoParentDims[0]->GetName() == "downtrack" &&
    8138          32 :                                apoParentDims[1]->GetName() == "crosstrack" &&
    8139           2 :                                apoParentDims[2]->GetName() == "bands");
    8140             : 
    8141             :     const size_t iYDimParent =
    8142          25 :         bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
    8143             :     const size_t iXDimParent =
    8144          25 :         bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
    8145             : 
    8146          77 :     for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
    8147             :     {
    8148          53 :         if (i == iYDimParent || i == iXDimParent)
    8149          48 :             continue;
    8150           5 :         if (apoNewDimsIn[i] == nullptr)
    8151             :         {
    8152           3 :             apoNewDims.emplace_back(aoParentDims[i]);
    8153             :         }
    8154           3 :         else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
    8155           1 :                  apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
    8156             :         {
    8157           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    8158             :                      "GetResampled(): apoNewDims[%u] should be the same "
    8159             :                      "as its parent",
    8160             :                      i);
    8161           1 :             return nullptr;
    8162             :         }
    8163             :         else
    8164             :         {
    8165           1 :             apoNewDims.emplace_back(aoParentDims[i]);
    8166             :         }
    8167           4 :         anBlockSize.emplace_back(anParentBlockSize[i]);
    8168             :     }
    8169             : 
    8170             :     std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
    8171          48 :         new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
    8172             : 
    8173          24 :     double dfXStart = 0.0;
    8174          24 :     double dfXSpacing = 0.0;
    8175          24 :     bool gotXSpacing = false;
    8176          48 :     auto poNewDimX = apoNewDimsIn[iXDimParent];
    8177          24 :     if (poNewDimX)
    8178             :     {
    8179           4 :         if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
    8180             :         {
    8181           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    8182             :                      "Too big size for X dimension");
    8183           0 :             return nullptr;
    8184             :         }
    8185           4 :         auto var = poNewDimX->GetIndexingVariable();
    8186           4 :         if (var)
    8187             :         {
    8188           2 :             if (var->GetDimensionCount() != 1 ||
    8189           2 :                 var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
    8190           1 :                 !var->IsRegularlySpaced(dfXStart, dfXSpacing))
    8191             :             {
    8192           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    8193             :                          "New X dimension should be indexed by a regularly "
    8194             :                          "spaced variable");
    8195           0 :                 return nullptr;
    8196             :             }
    8197           1 :             gotXSpacing = true;
    8198             :         }
    8199             :     }
    8200             : 
    8201          24 :     double dfYStart = 0.0;
    8202          24 :     double dfYSpacing = 0.0;
    8203          48 :     auto poNewDimY = apoNewDimsIn[iYDimParent];
    8204          24 :     bool gotYSpacing = false;
    8205          24 :     if (poNewDimY)
    8206             :     {
    8207           4 :         if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
    8208             :         {
    8209           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    8210             :                      "Too big size for Y dimension");
    8211           0 :             return nullptr;
    8212             :         }
    8213           4 :         auto var = poNewDimY->GetIndexingVariable();
    8214           4 :         if (var)
    8215             :         {
    8216           2 :             if (var->GetDimensionCount() != 1 ||
    8217           2 :                 var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
    8218           1 :                 !var->IsRegularlySpaced(dfYStart, dfYSpacing))
    8219             :             {
    8220           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    8221             :                          "New Y dimension should be indexed by a regularly "
    8222             :                          "spaced variable");
    8223           0 :                 return nullptr;
    8224             :             }
    8225           1 :             gotYSpacing = true;
    8226             :         }
    8227             :     }
    8228             : 
    8229             :     // This limitation could probably be removed
    8230          24 :     if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
    8231             :     {
    8232           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    8233             :                  "Either none of new X or Y dimension should have an indexing "
    8234             :                  "variable, or both should both should have one.");
    8235           0 :         return nullptr;
    8236             :     }
    8237             : 
    8238          48 :     std::string osDstWKT;
    8239          24 :     if (poTargetSRS)
    8240             :     {
    8241           2 :         char *pszDstWKT = nullptr;
    8242           2 :         if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
    8243             :         {
    8244           0 :             CPLFree(pszDstWKT);
    8245           0 :             return nullptr;
    8246             :         }
    8247           2 :         osDstWKT = pszDstWKT;
    8248           2 :         CPLFree(pszDstWKT);
    8249             :     }
    8250             : 
    8251             :     // Use coordinate variables for geolocation array
    8252          48 :     const auto apoCoordinateVars = poParent->GetCoordinateVariables();
    8253          24 :     bool useGeolocationArray = false;
    8254          24 :     if (apoCoordinateVars.size() >= 2)
    8255             :     {
    8256           0 :         std::shared_ptr<GDALMDArray> poLongVar;
    8257           0 :         std::shared_ptr<GDALMDArray> poLatVar;
    8258          15 :         for (const auto &poCoordVar : apoCoordinateVars)
    8259             :         {
    8260          10 :             const auto &osName = poCoordVar->GetName();
    8261          30 :             const auto poAttr = poCoordVar->GetAttribute("standard_name");
    8262          20 :             std::string osStandardName;
    8263          12 :             if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
    8264           2 :                 poAttr->GetDimensionCount() == 0)
    8265             :             {
    8266           2 :                 const char *pszStandardName = poAttr->ReadAsString();
    8267           2 :                 if (pszStandardName)
    8268           2 :                     osStandardName = pszStandardName;
    8269             :             }
    8270          21 :             if (osName == "lon" || osName == "longitude" ||
    8271          21 :                 osName == "Longitude" || osStandardName == "longitude")
    8272             :             {
    8273           5 :                 poLongVar = poCoordVar;
    8274             :             }
    8275           6 :             else if (osName == "lat" || osName == "latitude" ||
    8276           6 :                      osName == "Latitude" || osStandardName == "latitude")
    8277             :             {
    8278           5 :                 poLatVar = poCoordVar;
    8279             :             }
    8280             :         }
    8281           5 :         if (poLatVar != nullptr && poLongVar != nullptr)
    8282             :         {
    8283           5 :             const auto longDimCount = poLongVar->GetDimensionCount();
    8284           5 :             const auto &longDims = poLongVar->GetDimensions();
    8285           5 :             const auto latDimCount = poLatVar->GetDimensionCount();
    8286           5 :             const auto &latDims = poLatVar->GetDimensions();
    8287           5 :             const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
    8288           5 :             const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
    8289           0 :             if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
    8290           5 :                 latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
    8291             :             {
    8292             :                 // Geolocation arrays are 1D, and of consistent size with
    8293             :                 // the variable
    8294           0 :                 useGeolocationArray = true;
    8295             :             }
    8296           1 :             else if ((longDimCount == 2 ||
    8297           6 :                       (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
    8298          10 :                      longDims[longDimCount - 2]->GetSize() == yDimSize &&
    8299          10 :                      longDims[longDimCount - 1]->GetSize() == xDimSize &&
    8300           1 :                      (latDimCount == 2 ||
    8301           6 :                       (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
    8302          15 :                      latDims[latDimCount - 2]->GetSize() == yDimSize &&
    8303           5 :                      latDims[latDimCount - 1]->GetSize() == xDimSize)
    8304             : 
    8305             :             {
    8306             :                 // Geolocation arrays are 2D (or 3D with first dimension of
    8307             :                 // size 1, as found in Sentinel 5P products), and of consistent
    8308             :                 // size with the variable
    8309           5 :                 useGeolocationArray = true;
    8310             :             }
    8311             :             else
    8312             :             {
    8313           0 :                 CPLDebug(
    8314             :                     "GDAL",
    8315             :                     "Longitude and latitude coordinate variables found, "
    8316             :                     "but their characteristics are not compatible of using "
    8317             :                     "them as geolocation arrays");
    8318             :             }
    8319           5 :             if (useGeolocationArray)
    8320             :             {
    8321          10 :                 CPLDebug("GDAL",
    8322             :                          "Setting geolocation array from variables %s and %s",
    8323           5 :                          poLongVar->GetName().c_str(),
    8324           5 :                          poLatVar->GetName().c_str());
    8325             :                 const std::string osFilenameLong =
    8326           5 :                     VSIMemGenerateHiddenFilename("longitude.tif");
    8327             :                 const std::string osFilenameLat =
    8328           5 :                     VSIMemGenerateHiddenFilename("latitude.tif");
    8329             :                 std::unique_ptr<GDALDataset> poTmpLongDS(
    8330             :                     longDimCount == 1
    8331           0 :                         ? poLongVar->AsClassicDataset(0, 0)
    8332          20 :                         : poLongVar->AsClassicDataset(longDimCount - 1,
    8333          15 :                                                       longDimCount - 2));
    8334           5 :                 auto hTIFFLongDS = GDALTranslate(
    8335             :                     osFilenameLong.c_str(),
    8336             :                     GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
    8337             :                 std::unique_ptr<GDALDataset> poTmpLatDS(
    8338           0 :                     latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
    8339          20 :                                      : poLatVar->AsClassicDataset(
    8340          15 :                                            latDimCount - 1, latDimCount - 2));
    8341           5 :                 auto hTIFFLatDS = GDALTranslate(
    8342             :                     osFilenameLat.c_str(),
    8343             :                     GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
    8344           5 :                 const bool bError =
    8345           5 :                     (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
    8346           5 :                 GDALClose(hTIFFLongDS);
    8347           5 :                 GDALClose(hTIFFLatDS);
    8348           5 :                 if (bError)
    8349             :                 {
    8350           0 :                     VSIUnlink(osFilenameLong.c_str());
    8351           0 :                     VSIUnlink(osFilenameLat.c_str());
    8352           0 :                     return nullptr;
    8353             :                 }
    8354             : 
    8355           5 :                 poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
    8356             :             }
    8357             :         }
    8358             :         else
    8359             :         {
    8360           0 :             CPLDebug("GDAL",
    8361             :                      "Coordinate variables available for %s, but "
    8362             :                      "longitude and/or latitude variables were not identified",
    8363           0 :                      poParent->GetName().c_str());
    8364             :         }
    8365             :     }
    8366             : 
    8367             :     // Build gdalwarp arguments
    8368          48 :     CPLStringList aosArgv;
    8369             : 
    8370          24 :     aosArgv.AddString("-of");
    8371          24 :     aosArgv.AddString("VRT");
    8372             : 
    8373          24 :     aosArgv.AddString("-r");
    8374          24 :     aosArgv.AddString(pszResampleAlg);
    8375             : 
    8376          24 :     if (!osDstWKT.empty())
    8377             :     {
    8378           2 :         aosArgv.AddString("-t_srs");
    8379           2 :         aosArgv.AddString(osDstWKT.c_str());
    8380             :     }
    8381             : 
    8382          24 :     if (useGeolocationArray)
    8383           5 :         aosArgv.AddString("-geoloc");
    8384             : 
    8385          24 :     if (gotXSpacing && gotYSpacing)
    8386             :     {
    8387           1 :         const double dfXMin = dfXStart - dfXSpacing / 2;
    8388             :         const double dfXMax =
    8389           1 :             dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
    8390           1 :         const double dfYMax = dfYStart - dfYSpacing / 2;
    8391             :         const double dfYMin =
    8392           1 :             dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
    8393           1 :         aosArgv.AddString("-te");
    8394           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
    8395           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
    8396           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
    8397           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
    8398             :     }
    8399             : 
    8400          24 :     if (poNewDimX && poNewDimY)
    8401             :     {
    8402           3 :         aosArgv.AddString("-ts");
    8403             :         aosArgv.AddString(
    8404           3 :             CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
    8405             :         aosArgv.AddString(
    8406           3 :             CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
    8407             :     }
    8408          21 :     else if (poNewDimX)
    8409             :     {
    8410           1 :         aosArgv.AddString("-ts");
    8411             :         aosArgv.AddString(
    8412           1 :             CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
    8413           1 :         aosArgv.AddString("0");
    8414             :     }
    8415          20 :     else if (poNewDimY)
    8416             :     {
    8417           1 :         aosArgv.AddString("-ts");
    8418           1 :         aosArgv.AddString("0");
    8419             :         aosArgv.AddString(
    8420           1 :             CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
    8421             :     }
    8422             : 
    8423             :     // Create a warped VRT dataset
    8424             :     GDALWarpAppOptions *psOptions =
    8425          24 :         GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
    8426          24 :     GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
    8427             :     std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
    8428          48 :         GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
    8429          24 :     GDALWarpAppOptionsFree(psOptions);
    8430          24 :     if (poReprojectedDS == nullptr)
    8431           3 :         return nullptr;
    8432             : 
    8433             :     int nBlockXSize;
    8434             :     int nBlockYSize;
    8435          21 :     poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    8436          21 :     anBlockSize.emplace_back(nBlockYSize);
    8437          21 :     anBlockSize.emplace_back(nBlockXSize);
    8438             : 
    8439          21 :     GDALGeoTransform gt;
    8440          21 :     CPLErr eErr = poReprojectedDS->GetGeoTransform(gt);
    8441          21 :     CPLAssert(eErr == CE_None);
    8442          21 :     CPL_IGNORE_RET_VAL(eErr);
    8443             : 
    8444             :     auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
    8445           0 :         std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
    8446          42 :         poReprojectedDS->GetRasterYSize());
    8447             :     auto varY = GDALMDArrayRegularlySpaced::Create(
    8448          63 :         std::string(), poDimY->GetName(), poDimY, gt.yorig + gt.yscale / 2,
    8449          84 :         gt.yscale, 0);
    8450          21 :     poDimY->SetIndexingVariable(varY);
    8451             : 
    8452             :     auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
    8453           0 :         std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
    8454          42 :         poReprojectedDS->GetRasterXSize());
    8455             :     auto varX = GDALMDArrayRegularlySpaced::Create(
    8456          63 :         std::string(), poDimX->GetName(), poDimX, gt.xorig + gt.xscale / 2,
    8457          84 :         gt.xscale, 0);
    8458          21 :     poDimX->SetIndexingVariable(varX);
    8459             : 
    8460          21 :     apoNewDims.emplace_back(poDimY);
    8461          21 :     apoNewDims.emplace_back(poDimX);
    8462             :     auto newAr(std::shared_ptr<GDALMDArrayResampled>(
    8463          42 :         new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
    8464          21 :     newAr->SetSelf(newAr);
    8465          21 :     if (poTargetSRS)
    8466             :     {
    8467           2 :         newAr->m_poSRS.reset(poTargetSRS->Clone());
    8468             :     }
    8469             :     else
    8470             :     {
    8471          19 :         newAr->m_poSRS = poParent->GetSpatialRef();
    8472             :     }
    8473          21 :     newAr->m_poVarX = varX;
    8474          21 :     newAr->m_poVarY = varY;
    8475          21 :     newAr->m_poReprojectedDS = std::move(poReprojectedDS);
    8476          21 :     newAr->m_poParentDS = std::move(poParentDS);
    8477             : 
    8478             :     // If the input array is y,x,band ordered, the above newAr is
    8479             :     // actually band,y,x ordered as it is more convenient for
    8480             :     // GDALMDArrayResampled::IRead() implementation. But transpose that
    8481             :     // array to the order of the input array
    8482          21 :     if (bYXBandOrder)
    8483           4 :         return newAr->Transpose(std::vector<int>{1, 2, 0});
    8484             : 
    8485          19 :     return newAr;
    8486             : }
    8487             : 
    8488             : /************************************************************************/
    8489             : /*                    GDALMDArrayResampled::IRead()                     */
    8490             : /************************************************************************/
    8491             : 
    8492          29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
    8493             :                                  const size_t *count, const GInt64 *arrayStep,
    8494             :                                  const GPtrDiff_t *bufferStride,
    8495             :                                  const GDALExtendedDataType &bufferDataType,
    8496             :                                  void *pDstBuffer) const
    8497             : {
    8498          29 :     if (bufferDataType.GetClass() != GEDTC_NUMERIC)
    8499           0 :         return false;
    8500             : 
    8501             :     struct Stack
    8502             :     {
    8503             :         size_t nIters = 0;
    8504             :         GByte *dst_ptr = nullptr;
    8505             :         GPtrDiff_t dst_inc_offset = 0;
    8506             :     };
    8507             : 
    8508          29 :     const auto nDims = GetDimensionCount();
    8509          58 :     std::vector<Stack> stack(nDims + 1);  // +1 to avoid -Wnull-dereference
    8510          29 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    8511          92 :     for (size_t i = 0; i < nDims; i++)
    8512             :     {
    8513          63 :         stack[i].dst_inc_offset =
    8514          63 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    8515             :     }
    8516          29 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    8517             : 
    8518          29 :     size_t dimIdx = 0;
    8519          29 :     const size_t iDimY = nDims - 2;
    8520          29 :     const size_t iDimX = nDims - 1;
    8521             :     // Use an array to avoid a false positive warning from CLang Static
    8522             :     // Analyzer about flushCaches being never read
    8523          29 :     bool flushCaches[] = {false};
    8524             :     const bool bYXBandOrder =
    8525          29 :         m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
    8526             : 
    8527          38 : lbl_next_depth:
    8528          38 :     if (dimIdx == iDimY)
    8529             :     {
    8530          33 :         if (flushCaches[0])
    8531             :         {
    8532           5 :             flushCaches[0] = false;
    8533             :             // When changing of 2D slice, flush GDAL 2D buffers
    8534           5 :             m_poParentDS->FlushCache(false);
    8535           5 :             m_poReprojectedDS->FlushCache(false);
    8536             :         }
    8537             : 
    8538          33 :         if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
    8539             :                                     GF_Read, iDimX, iDimY, arrayStartIdx, count,
    8540             :                                     arrayStep, bufferStride, bufferDataType,
    8541          33 :                                     stack[dimIdx].dst_ptr))
    8542             :         {
    8543           0 :             return false;
    8544             :         }
    8545             :     }
    8546             :     else
    8547             :     {
    8548           5 :         stack[dimIdx].nIters = count[dimIdx];
    8549           5 :         if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
    8550           5 :             arrayStartIdx[dimIdx])
    8551             :         {
    8552           1 :             flushCaches[0] = true;
    8553             :         }
    8554           5 :         m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
    8555           5 :             arrayStartIdx[dimIdx];
    8556             :         while (true)
    8557             :         {
    8558           9 :             dimIdx++;
    8559           9 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    8560           9 :             goto lbl_next_depth;
    8561           9 :         lbl_return_to_caller:
    8562           9 :             dimIdx--;
    8563           9 :             if ((--stack[dimIdx].nIters) == 0)
    8564           5 :                 break;
    8565           4 :             flushCaches[0] = true;
    8566           4 :             ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
    8567           4 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    8568             :         }
    8569             :     }
    8570          38 :     if (dimIdx > 0)
    8571           9 :         goto lbl_return_to_caller;
    8572             : 
    8573          29 :     return true;
    8574             : }
    8575             : 
    8576             : /************************************************************************/
    8577             : /*                            GetResampled()                            */
    8578             : /************************************************************************/
    8579             : 
    8580             : /** Return an array that is a resampled / reprojected view of the current array
    8581             :  *
    8582             :  * This is the same as the C function GDALMDArrayGetResampled().
    8583             :  *
    8584             :  * Currently this method can only resample along the last 2 dimensions, unless
    8585             :  * orthorectifying a NASA EMIT dataset.
    8586             :  *
    8587             :  * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
    8588             :  * geometry lookup table (GLT) is used by default for fast orthorectification.
    8589             :  *
    8590             :  * Options available are:
    8591             :  * <ul>
    8592             :  * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
    8593             :  * Can be set to NO to use generic reprojection method.
    8594             :  * </li>
    8595             :  * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
    8596             :  * orthorectification to take into account the value of the
    8597             :  * /sensor_band_parameters/good_wavelengths array to decide if slices of the
    8598             :  * current array along the band dimension are valid.</li>
    8599             :  * </ul>
    8600             :  *
    8601             :  * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
    8602             :  *                   apoNewDims[i] can be NULL to let the method automatically
    8603             :  *                   determine it.
    8604             :  * @param resampleAlg Resampling algorithm
    8605             :  * @param poTargetSRS Target SRS, or nullptr
    8606             :  * @param papszOptions NULL-terminated list of options, or NULL.
    8607             :  *
    8608             :  * @return a new array, that holds a reference to the original one, and thus is
    8609             :  * a view of it (not a copy), or nullptr in case of error.
    8610             :  *
    8611             :  * @since 3.4
    8612             :  */
    8613          38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
    8614             :     const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
    8615             :     GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
    8616             :     CSLConstList papszOptions) const
    8617             : {
    8618          76 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    8619          38 :     if (!self)
    8620             :     {
    8621           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8622             :                  "Driver implementation issue: m_pSelf not set !");
    8623           0 :         return nullptr;
    8624             :     }
    8625          38 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    8626             :     {
    8627           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8628             :                  "GetResampled() only supports numeric data type");
    8629           0 :         return nullptr;
    8630             :     }
    8631             : 
    8632             :     // Special case for NASA EMIT datasets
    8633          76 :     auto apoDims = GetDimensions();
    8634          36 :     if (poTargetSRS == nullptr &&
    8635          59 :         ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
    8636          20 :           apoDims[1]->GetName() == "crosstrack" &&
    8637          10 :           apoDims[2]->GetName() == "bands" &&
    8638          48 :           (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
    8639           1 :            apoNewDims ==
    8640          42 :                std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
    8641          30 :                                                            apoDims[2]})) ||
    8642          51 :          (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
    8643           3 :           apoDims[1]->GetName() == "crosstrack" &&
    8644          77 :           apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
    8645          13 :         CPLTestBool(CSLFetchNameValueDef(papszOptions,
    8646             :                                          "EMIT_ORTHORECTIFICATION", "YES")))
    8647             :     {
    8648           9 :         auto poRootGroup = GetRootGroup();
    8649           9 :         if (poRootGroup)
    8650             :         {
    8651          18 :             auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
    8652          18 :             auto poLocationGroup = poRootGroup->OpenGroup("location");
    8653           9 :             if (poAttrGeotransform &&
    8654           9 :                 poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
    8655           9 :                 poAttrGeotransform->GetDimensionCount() == 1 &&
    8656          27 :                 poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
    8657           9 :                 poLocationGroup)
    8658             :             {
    8659          18 :                 auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
    8660          18 :                 auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
    8661          27 :                 if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
    8662          18 :                     poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
    8663          18 :                     poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
    8664          27 :                     poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
    8665          27 :                     poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
    8666           9 :                     poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
    8667             :                 {
    8668             :                     return CreateGLTOrthorectified(
    8669             :                         self, poRootGroup, poGLT_X, poGLT_Y,
    8670             :                         /* nGLTIndexOffset = */ -1,
    8671          18 :                         poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
    8672             :                 }
    8673             :             }
    8674             :         }
    8675             :     }
    8676             : 
    8677          29 :     if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
    8678             :                                          "EMIT_ORTHORECTIFICATION", "NO")))
    8679             :     {
    8680           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8681             :                  "EMIT_ORTHORECTIFICATION required, but dataset and/or "
    8682             :                  "parameters are not compatible with it");
    8683           0 :         return nullptr;
    8684             :     }
    8685             : 
    8686             :     return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
    8687          29 :                                         poTargetSRS, papszOptions);
    8688             : }
    8689             : 
    8690             : /************************************************************************/
    8691             : /*                        GDALDatasetFromArray()                        */
    8692             : /************************************************************************/
    8693             : 
    8694             : class GDALDatasetFromArray;
    8695             : 
    8696             : namespace
    8697             : {
    8698             : struct MetadataItem
    8699             : {
    8700             :     std::shared_ptr<GDALAbstractMDArray> poArray{};
    8701             :     std::string osName{};
    8702             :     std::string osDefinition{};
    8703             :     bool bDefinitionUsesPctForG = false;
    8704             : };
    8705             : 
    8706             : struct BandImageryMetadata
    8707             : {
    8708             :     std::shared_ptr<GDALAbstractMDArray> poCentralWavelengthArray{};
    8709             :     double dfCentralWavelengthToMicrometer = 1.0;
    8710             :     std::shared_ptr<GDALAbstractMDArray> poFWHMArray{};
    8711             :     double dfFWHMToMicrometer = 1.0;
    8712             : };
    8713             : 
    8714             : }  // namespace
    8715             : 
    8716             : class GDALRasterBandFromArray final : public GDALPamRasterBand
    8717             : {
    8718             :     std::vector<GUInt64> m_anOffset{};
    8719             :     std::vector<size_t> m_anCount{};
    8720             :     std::vector<GPtrDiff_t> m_anStride{};
    8721             : 
    8722             :   protected:
    8723             :     CPLErr IReadBlock(int, int, void *) override;
    8724             :     CPLErr IWriteBlock(int, int, void *) override;
    8725             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
    8726             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
    8727             :                      GDALDataType eBufType, GSpacing nPixelSpaceBuf,
    8728             :                      GSpacing nLineSpaceBuf,
    8729             :                      GDALRasterIOExtraArg *psExtraArg) override;
    8730             : 
    8731             :   public:
    8732             :     explicit GDALRasterBandFromArray(
    8733             :         GDALDatasetFromArray *poDSIn,
    8734             :         const std::vector<GUInt64> &anOtherDimCoord,
    8735             :         const std::vector<std::vector<MetadataItem>>
    8736             :             &aoBandParameterMetadataItems,
    8737             :         const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
    8738             :         double dfDelay, time_t nStartTime, bool &bHasWarned);
    8739             : 
    8740             :     double GetNoDataValue(int *pbHasNoData) override;
    8741             :     int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
    8742             :     uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
    8743             :     double GetOffset(int *pbHasOffset) override;
    8744             :     double GetScale(int *pbHasScale) override;
    8745             :     const char *GetUnitType() override;
    8746             :     GDALColorInterp GetColorInterpretation() override;
    8747             :     int GetOverviewCount() override;
    8748             :     GDALRasterBand *GetOverview(int idx) override;
    8749             :     CPLErr AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
    8750             :                       int nBufXSize, int nBufYSize, GDALDataType eBufType,
    8751             :                       CSLConstList papszOptions) override;
    8752             : };
    8753             : 
    8754             : class GDALDatasetFromArray final : public GDALPamDataset
    8755             : {
    8756             :     friend class GDALRasterBandFromArray;
    8757             : 
    8758             :     std::shared_ptr<GDALMDArray> m_poArray;
    8759             :     const size_t m_iXDim;
    8760             :     const size_t m_iYDim;
    8761             :     const CPLStringList m_aosOptions;
    8762             :     GDALGeoTransform m_gt{};
    8763             :     bool m_bHasGT = false;
    8764             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
    8765             :     GDALMultiDomainMetadata m_oMDD{};
    8766             :     std::string m_osOvrFilename{};
    8767             :     bool m_bOverviewsDiscovered = false;
    8768             :     std::vector<std::unique_ptr<GDALDataset, GDALDatasetUniquePtrReleaser>>
    8769             :         m_apoOverviews{};
    8770             : 
    8771             :   public:
    8772         474 :     GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
    8773             :                          size_t iXDim, size_t iYDim,
    8774             :                          const CPLStringList &aosOptions)
    8775         474 :         : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
    8776         474 :           m_aosOptions(aosOptions)
    8777             :     {
    8778             :         // Initialize an overview filename from the filename of the array
    8779             :         // and its name.
    8780         474 :         const std::string &osFilename = m_poArray->GetFilename();
    8781         474 :         if (!osFilename.empty())
    8782             :         {
    8783         432 :             m_osOvrFilename = osFilename;
    8784         432 :             m_osOvrFilename += '.';
    8785       25213 :             for (char ch : m_poArray->GetName())
    8786             :             {
    8787       24781 :                 if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
    8788       21657 :                     (ch >= 'a' && ch <= 'z') || ch == '_')
    8789             :                 {
    8790       19594 :                     m_osOvrFilename += ch;
    8791             :                 }
    8792             :                 else
    8793             :                 {
    8794        5187 :                     m_osOvrFilename += '_';
    8795             :                 }
    8796             :             }
    8797         432 :             m_osOvrFilename += ".ovr";
    8798         432 :             oOvManager.Initialize(this);
    8799             :         }
    8800         474 :     }
    8801             : 
    8802             :     static std::unique_ptr<GDALDatasetFromArray>
    8803             :     Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
    8804             :            size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
    8805             :            CSLConstList papszOptions);
    8806             : 
    8807             :     ~GDALDatasetFromArray() override;
    8808             : 
    8809         669 :     CPLErr Close(GDALProgressFunc = nullptr, void * = nullptr) override
    8810             :     {
    8811         669 :         CPLErr eErr = CE_None;
    8812         669 :         if (nOpenFlags != OPEN_FLAGS_CLOSED)
    8813             :         {
    8814         669 :             if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
    8815             :                 CE_None)
    8816           0 :                 eErr = CE_Failure;
    8817         669 :             m_poArray.reset();
    8818             :         }
    8819         669 :         return eErr;
    8820             :     }
    8821             : 
    8822          83 :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override
    8823             :     {
    8824          83 :         gt = m_gt;
    8825          83 :         return m_bHasGT ? CE_None : CE_Failure;
    8826             :     }
    8827             : 
    8828          93 :     const OGRSpatialReference *GetSpatialRef() const override
    8829             :     {
    8830          93 :         if (m_poArray->GetDimensionCount() < 2)
    8831           3 :             return nullptr;
    8832          90 :         m_poSRS = m_poArray->GetSpatialRef();
    8833          90 :         if (m_poSRS)
    8834             :         {
    8835          46 :             m_poSRS.reset(m_poSRS->Clone());
    8836          92 :             auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
    8837         138 :             for (auto &m : axisMapping)
    8838             :             {
    8839          92 :                 if (m == static_cast<int>(m_iXDim) + 1)
    8840          46 :                     m = 1;
    8841          46 :                 else if (m == static_cast<int>(m_iYDim) + 1)
    8842          46 :                     m = 2;
    8843             :             }
    8844          46 :             m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
    8845             :         }
    8846          90 :         return m_poSRS.get();
    8847             :     }
    8848             : 
    8849           9 :     CPLErr SetMetadata(CSLConstList papszMetadata,
    8850             :                        const char *pszDomain) override
    8851             :     {
    8852           9 :         return m_oMDD.SetMetadata(papszMetadata, pszDomain);
    8853             :     }
    8854             : 
    8855         184 :     CSLConstList GetMetadata(const char *pszDomain) override
    8856             :     {
    8857         184 :         return m_oMDD.GetMetadata(pszDomain);
    8858             :     }
    8859             : 
    8860         258 :     const char *GetMetadataItem(const char *pszName,
    8861             :                                 const char *pszDomain) override
    8862             :     {
    8863         469 :         if (!m_osOvrFilename.empty() && pszName &&
    8864         492 :             EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
    8865          23 :             EQUAL(pszDomain, "OVERVIEWS"))
    8866             :         {
    8867          23 :             return m_osOvrFilename.c_str();
    8868             :         }
    8869         235 :         return m_oMDD.GetMetadataItem(pszName, pszDomain);
    8870             :     }
    8871             : 
    8872           2 :     CPLErr IBuildOverviews(const char *pszResampling, int nOverviews,
    8873             :                            const int *panOverviewList, int nListBands,
    8874             :                            const int *panBandList, GDALProgressFunc pfnProgress,
    8875             :                            void *pProgressData,
    8876             :                            CSLConstList papszOptions) override
    8877             :     {
    8878             :         // Try the multidimensional array path. Use quiet handler to
    8879             :         // suppress the "not supported" error from the base class stub.
    8880           2 :         bool bNotSupported = false;
    8881           4 :         std::string osErrMsg;
    8882           2 :         CPLErr eSavedClass = CE_None;
    8883           2 :         int nSavedNo = CPLE_None;
    8884             :         {
    8885           2 :             CPLErrorHandlerPusher oQuiet(CPLQuietErrorHandler);
    8886           2 :             CPLErr eErr = m_poArray->BuildOverviews(
    8887             :                 pszResampling, nOverviews, panOverviewList, pfnProgress,
    8888           2 :                 pProgressData, papszOptions);
    8889           2 :             if (eErr == CE_None)
    8890             :             {
    8891           1 :                 m_bOverviewsDiscovered = false;
    8892           1 :                 m_apoOverviews.clear();
    8893           1 :                 return CE_None;
    8894             :             }
    8895           1 :             nSavedNo = CPLGetLastErrorNo();
    8896           1 :             eSavedClass = CPLGetLastErrorType();
    8897           1 :             osErrMsg = CPLGetLastErrorMsg();
    8898           1 :             bNotSupported = (nSavedNo == CPLE_NotSupported);
    8899             :         }
    8900           1 :         if (!bNotSupported)
    8901             :         {
    8902             :             // Re-emit the error that was suppressed by the quiet handler.
    8903           0 :             CPLError(eSavedClass, nSavedNo, "%s", osErrMsg.c_str());
    8904           0 :             return CE_Failure;
    8905             :         }
    8906             :         // Driver doesn't implement BuildOverviews - fall back to
    8907             :         // default path (e.g. external .ovr file).
    8908           1 :         CPLErrorReset();
    8909           1 :         return GDALDataset::IBuildOverviews(
    8910             :             pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
    8911           1 :             pfnProgress, pProgressData, papszOptions);
    8912             :     }
    8913             : 
    8914          68 :     void DiscoverOverviews()
    8915             :     {
    8916          68 :         if (!m_bOverviewsDiscovered)
    8917             :         {
    8918          23 :             m_bOverviewsDiscovered = true;
    8919          23 :             if (const int nOverviews = m_poArray->GetOverviewCount())
    8920             :             {
    8921          14 :                 if (auto poRootGroup = m_poArray->GetRootGroup())
    8922             :                 {
    8923           7 :                     const size_t nDims = m_poArray->GetDimensionCount();
    8924          14 :                     CPLStringList aosOptions(m_aosOptions);
    8925           7 :                     aosOptions.SetNameValue("LOAD_PAM", "NO");
    8926          18 :                     for (int iOvr = 0; iOvr < nOverviews; ++iOvr)
    8927             :                     {
    8928          22 :                         if (auto poOvrArray = m_poArray->GetOverview(iOvr))
    8929             :                         {
    8930          22 :                             if (poOvrArray->GetDimensionCount() == nDims &&
    8931          11 :                                 poOvrArray->GetDataType() ==
    8932          11 :                                     m_poArray->GetDataType())
    8933             :                             {
    8934             :                                 auto poOvrDS =
    8935          11 :                                     Create(poOvrArray, m_iXDim, m_iYDim,
    8936          22 :                                            poRootGroup, aosOptions);
    8937          11 :                                 if (poOvrDS)
    8938             :                                 {
    8939          11 :                                     m_apoOverviews.push_back(
    8940          22 :                                         std::unique_ptr<
    8941             :                                             GDALDataset,
    8942             :                                             GDALDatasetUniquePtrReleaser>(
    8943          11 :                                             poOvrDS.release()));
    8944             :                                 }
    8945             :                             }
    8946             :                         }
    8947             :                     }
    8948             :                 }
    8949             :             }
    8950             :         }
    8951          68 :     }
    8952             : };
    8953             : 
    8954         948 : GDALDatasetFromArray::~GDALDatasetFromArray()
    8955             : {
    8956         474 :     GDALDatasetFromArray::Close();
    8957         948 : }
    8958             : 
    8959             : /************************************************************************/
    8960             : /*                      GDALRasterBandFromArray()                       */
    8961             : /************************************************************************/
    8962             : 
    8963         563 : GDALRasterBandFromArray::GDALRasterBandFromArray(
    8964             :     GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
    8965             :     const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
    8966             :     const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
    8967         563 :     double dfDelay, time_t nStartTime, bool &bHasWarned)
    8968             : {
    8969         563 :     const auto &poArray(poDSIn->m_poArray);
    8970         563 :     const auto &dims(poArray->GetDimensions());
    8971         563 :     const auto nDimCount(dims.size());
    8972        1126 :     const auto blockSize(poArray->GetBlockSize());
    8973             : 
    8974         544 :     nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
    8975        1107 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    8976         387 :                                                   blockSize[poDSIn->m_iYDim]))
    8977             :                       : 1;
    8978         563 :     nBlockXSize = blockSize[poDSIn->m_iXDim]
    8979         404 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    8980         404 :                                                   blockSize[poDSIn->m_iXDim]))
    8981         563 :                       : poDSIn->GetRasterXSize();
    8982             : 
    8983         563 :     eDataType = poArray->GetDataType().GetNumericDataType();
    8984         563 :     const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
    8985             : 
    8986         563 :     if (nDTSize > 0)
    8987             :     {
    8988             :         // If the above computed block size exceeds INT_MAX or 1/100th of the
    8989             :         // maximum allowed size for the block cache, divide its shape by two,
    8990             :         // along the largest dimension. Only do that while there are at least
    8991             :         // one dimension with 2 pixels.
    8992          12 :         while (
    8993        1105 :             (nBlockXSize >= 2 || nBlockYSize >= 2) &&
    8994         530 :             (nBlockXSize > INT_MAX / nBlockYSize / nDTSize ||
    8995         527 :              (nBlockXSize > GDALGetCacheMax64() / 100 / nBlockYSize / nDTSize)))
    8996             :         {
    8997          12 :             if (nBlockXSize > nBlockYSize)
    8998          12 :                 nBlockXSize /= 2;
    8999             :             else
    9000           0 :                 nBlockYSize /= 2;
    9001             :         }
    9002             :     }
    9003             : 
    9004         563 :     eAccess = poDSIn->eAccess;
    9005         563 :     m_anOffset.resize(nDimCount);
    9006         563 :     m_anCount.resize(nDimCount, 1);
    9007         563 :     m_anStride.resize(nDimCount);
    9008        1821 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    9009             :     {
    9010        1258 :         if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
    9011             :         {
    9012         302 :             std::string dimName(dims[i]->GetName());
    9013         151 :             GUInt64 nIndex = anOtherDimCoord[j];
    9014             :             // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
    9015             :             // subsetted dimensions as generated by GetView()
    9016         151 :             if (STARTS_WITH(dimName.c_str(), "subset_"))
    9017             :             {
    9018             :                 CPLStringList aosTokens(
    9019          12 :                     CSLTokenizeString2(dimName.c_str(), "_", 0));
    9020           6 :                 if (aosTokens.size() == 5)
    9021             :                 {
    9022           6 :                     dimName = aosTokens[1];
    9023          18 :                     const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
    9024           6 :                         aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
    9025           6 :                     const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
    9026           6 :                     nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
    9027           0 :                                           : nStartDim - (nIndex * -nIncrDim);
    9028             :                 }
    9029             :             }
    9030         151 :             if (nDimCount != 3 || dimName != "Band")
    9031             :             {
    9032          90 :                 SetMetadataItem(
    9033             :                     CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
    9034             :                     CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
    9035             :             }
    9036             : 
    9037         151 :             auto indexingVar = dims[i]->GetIndexingVariable();
    9038             : 
    9039             :             // If the indexing variable is also listed in band parameter arrays,
    9040             :             // then don't use our default formatting
    9041         151 :             if (indexingVar)
    9042             :             {
    9043          49 :                 for (const auto &oItem : aoBandParameterMetadataItems[j])
    9044             :                 {
    9045          14 :                     if (oItem.poArray->GetFullName() ==
    9046          14 :                         indexingVar->GetFullName())
    9047             :                     {
    9048          12 :                         indexingVar.reset();
    9049          12 :                         break;
    9050             :                     }
    9051             :                 }
    9052             :             }
    9053             : 
    9054         186 :             if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
    9055          35 :                 indexingVar->GetDimensions()[0]->GetSize() ==
    9056          35 :                     dims[i]->GetSize())
    9057             :             {
    9058          35 :                 if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
    9059             :                 {
    9060           0 :                     if (!bHasWarned)
    9061             :                     {
    9062           0 :                         CPLError(
    9063             :                             CE_Warning, CPLE_AppDefined,
    9064             :                             "Maximum delay to load band metadata from "
    9065             :                             "dimension indexing variables has expired. "
    9066             :                             "Increase the value of the "
    9067             :                             "LOAD_EXTRA_DIM_METADATA_DELAY "
    9068             :                             "option of GDALMDArray::AsClassicDataset() "
    9069             :                             "(also accessible as the "
    9070             :                             "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
    9071             :                             "configuration option), "
    9072             :                             "or set it to 'unlimited' for unlimited delay. ");
    9073           0 :                         bHasWarned = true;
    9074             :                     }
    9075             :                 }
    9076             :                 else
    9077             :                 {
    9078          35 :                     size_t nCount = 1;
    9079          35 :                     const auto &dt(indexingVar->GetDataType());
    9080          70 :                     std::vector<GByte> abyTmp(dt.GetSize());
    9081          70 :                     if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
    9082          35 :                                           nullptr, nullptr, dt, &abyTmp[0]))
    9083             :                     {
    9084          35 :                         char *pszTmp = nullptr;
    9085          35 :                         GDALExtendedDataType::CopyValue(
    9086          35 :                             &abyTmp[0], dt, &pszTmp,
    9087          70 :                             GDALExtendedDataType::CreateString());
    9088          35 :                         dt.FreeDynamicMemory(abyTmp.data());
    9089          35 :                         if (pszTmp)
    9090             :                         {
    9091          35 :                             SetMetadataItem(
    9092             :                                 CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
    9093             :                                 pszTmp);
    9094          35 :                             CPLFree(pszTmp);
    9095             :                         }
    9096             : 
    9097          35 :                         const auto &unit(indexingVar->GetUnit());
    9098          35 :                         if (!unit.empty())
    9099             :                         {
    9100          12 :                             SetMetadataItem(
    9101             :                                 CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
    9102             :                                 unit.c_str());
    9103             :                         }
    9104             :                     }
    9105             :                 }
    9106             :             }
    9107             : 
    9108         169 :             for (const auto &oItem : aoBandParameterMetadataItems[j])
    9109             :             {
    9110          36 :                 CPLString osVal;
    9111             : 
    9112          18 :                 size_t nCount = 1;
    9113          18 :                 const auto &dt(oItem.poArray->GetDataType());
    9114          18 :                 if (oItem.bDefinitionUsesPctForG)
    9115             :                 {
    9116             :                     // There is one and only one %[x][.y]f|g in osDefinition
    9117          16 :                     std::vector<GByte> abyTmp(dt.GetSize());
    9118          16 :                     if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
    9119           8 :                                             nullptr, nullptr, dt, &abyTmp[0]))
    9120             :                     {
    9121           8 :                         double dfVal = 0;
    9122           8 :                         GDALExtendedDataType::CopyValue(
    9123           8 :                             &abyTmp[0], dt, &dfVal,
    9124          16 :                             GDALExtendedDataType::Create(GDT_Float64));
    9125           8 :                         osVal.Printf(oItem.osDefinition.c_str(), dfVal);
    9126           8 :                         dt.FreeDynamicMemory(abyTmp.data());
    9127             :                     }
    9128             :                 }
    9129             :                 else
    9130             :                 {
    9131             :                     // There should be zero or one %s in osDefinition
    9132          10 :                     char *pszValue = nullptr;
    9133          10 :                     if (dt.GetClass() == GEDTC_STRING)
    9134             :                     {
    9135           4 :                         CPL_IGNORE_RET_VAL(oItem.poArray->Read(
    9136           2 :                             &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
    9137           2 :                             dt, &pszValue));
    9138             :                     }
    9139             :                     else
    9140             :                     {
    9141          16 :                         std::vector<GByte> abyTmp(dt.GetSize());
    9142          16 :                         if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
    9143             :                                                 nullptr, nullptr, dt,
    9144           8 :                                                 &abyTmp[0]))
    9145             :                         {
    9146           8 :                             GDALExtendedDataType::CopyValue(
    9147           8 :                                 &abyTmp[0], dt, &pszValue,
    9148          16 :                                 GDALExtendedDataType::CreateString());
    9149             :                         }
    9150             :                     }
    9151             : 
    9152          10 :                     if (pszValue)
    9153             :                     {
    9154          10 :                         osVal.Printf(oItem.osDefinition.c_str(), pszValue);
    9155          10 :                         CPLFree(pszValue);
    9156             :                     }
    9157             :                 }
    9158          18 :                 if (!osVal.empty())
    9159          18 :                     SetMetadataItem(oItem.osName.c_str(), osVal);
    9160             :             }
    9161             : 
    9162         151 :             if (aoBandImageryMetadata[j].poCentralWavelengthArray)
    9163             :             {
    9164             :                 auto &poCentralWavelengthArray =
    9165           4 :                     aoBandImageryMetadata[j].poCentralWavelengthArray;
    9166           4 :                 size_t nCount = 1;
    9167           4 :                 const auto &dt(poCentralWavelengthArray->GetDataType());
    9168           8 :                 std::vector<GByte> abyTmp(dt.GetSize());
    9169           8 :                 if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
    9170             :                                                    &nCount, nullptr, nullptr,
    9171           4 :                                                    dt, &abyTmp[0]))
    9172             :                 {
    9173           4 :                     double dfVal = 0;
    9174           4 :                     GDALExtendedDataType::CopyValue(
    9175           4 :                         &abyTmp[0], dt, &dfVal,
    9176           8 :                         GDALExtendedDataType::Create(GDT_Float64));
    9177           4 :                     dt.FreeDynamicMemory(abyTmp.data());
    9178           4 :                     SetMetadataItem(
    9179             :                         "CENTRAL_WAVELENGTH_UM",
    9180             :                         CPLSPrintf(
    9181           4 :                             "%g", dfVal * aoBandImageryMetadata[j]
    9182           4 :                                               .dfCentralWavelengthToMicrometer),
    9183             :                         "IMAGERY");
    9184             :                 }
    9185             :             }
    9186             : 
    9187         151 :             if (aoBandImageryMetadata[j].poFWHMArray)
    9188             :             {
    9189           2 :                 auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
    9190           2 :                 size_t nCount = 1;
    9191           2 :                 const auto &dt(poFWHMArray->GetDataType());
    9192           4 :                 std::vector<GByte> abyTmp(dt.GetSize());
    9193           4 :                 if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
    9194           2 :                                       nullptr, dt, &abyTmp[0]))
    9195             :                 {
    9196           2 :                     double dfVal = 0;
    9197           2 :                     GDALExtendedDataType::CopyValue(
    9198           2 :                         &abyTmp[0], dt, &dfVal,
    9199           4 :                         GDALExtendedDataType::Create(GDT_Float64));
    9200           2 :                     dt.FreeDynamicMemory(abyTmp.data());
    9201           2 :                     SetMetadataItem(
    9202             :                         "FWHM_UM",
    9203           2 :                         CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
    9204           2 :                                                      .dfFWHMToMicrometer),
    9205             :                         "IMAGERY");
    9206             :                 }
    9207             :             }
    9208             : 
    9209         151 :             m_anOffset[i] = anOtherDimCoord[j];
    9210         151 :             j++;
    9211             :         }
    9212             :     }
    9213         563 : }
    9214             : 
    9215             : /************************************************************************/
    9216             : /*                           GetNoDataValue()                           */
    9217             : /************************************************************************/
    9218             : 
    9219         158 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
    9220             : {
    9221         158 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9222         158 :     const auto &poArray(l_poDS->m_poArray);
    9223         158 :     bool bHasNodata = false;
    9224         158 :     const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
    9225         158 :     if (pbHasNoData)
    9226         142 :         *pbHasNoData = bHasNodata;
    9227         158 :     return res;
    9228             : }
    9229             : 
    9230             : /************************************************************************/
    9231             : /*                       GetNoDataValueAsInt64()                        */
    9232             : /************************************************************************/
    9233             : 
    9234           1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
    9235             : {
    9236           1 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9237           1 :     const auto &poArray(l_poDS->m_poArray);
    9238           1 :     bool bHasNodata = false;
    9239           1 :     const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
    9240           1 :     if (pbHasNoData)
    9241           1 :         *pbHasNoData = bHasNodata;
    9242           1 :     return nodata;
    9243             : }
    9244             : 
    9245             : /************************************************************************/
    9246             : /*                       GetNoDataValueAsUInt64()                       */
    9247             : /************************************************************************/
    9248             : 
    9249           1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
    9250             : {
    9251           1 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9252           1 :     const auto &poArray(l_poDS->m_poArray);
    9253           1 :     bool bHasNodata = false;
    9254           1 :     const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
    9255           1 :     if (pbHasNoData)
    9256           1 :         *pbHasNoData = bHasNodata;
    9257           1 :     return nodata;
    9258             : }
    9259             : 
    9260             : /************************************************************************/
    9261             : /*                             GetOffset()                              */
    9262             : /************************************************************************/
    9263             : 
    9264          42 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
    9265             : {
    9266          42 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9267          42 :     const auto &poArray(l_poDS->m_poArray);
    9268          42 :     bool bHasValue = false;
    9269          42 :     double dfRes = poArray->GetOffset(&bHasValue);
    9270          42 :     if (pbHasOffset)
    9271          23 :         *pbHasOffset = bHasValue;
    9272          42 :     return dfRes;
    9273             : }
    9274             : 
    9275             : /************************************************************************/
    9276             : /*                            GetUnitType()                             */
    9277             : /************************************************************************/
    9278             : 
    9279          52 : const char *GDALRasterBandFromArray::GetUnitType()
    9280             : {
    9281          52 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9282          52 :     const auto &poArray(l_poDS->m_poArray);
    9283          52 :     return poArray->GetUnit().c_str();
    9284             : }
    9285             : 
    9286             : /************************************************************************/
    9287             : /*                              GetScale()                              */
    9288             : /************************************************************************/
    9289             : 
    9290          40 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
    9291             : {
    9292          40 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9293          40 :     const auto &poArray(l_poDS->m_poArray);
    9294          40 :     bool bHasValue = false;
    9295          40 :     double dfRes = poArray->GetScale(&bHasValue);
    9296          40 :     if (pbHasScale)
    9297          21 :         *pbHasScale = bHasValue;
    9298          40 :     return dfRes;
    9299             : }
    9300             : 
    9301             : /************************************************************************/
    9302             : /*                             IReadBlock()                             */
    9303             : /************************************************************************/
    9304             : 
    9305         102 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
    9306             :                                            void *pImage)
    9307             : {
    9308         102 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    9309         102 :     const int nXOff = nBlockXOff * nBlockXSize;
    9310         102 :     const int nYOff = nBlockYOff * nBlockYSize;
    9311         102 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    9312         102 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    9313             :     GDALRasterIOExtraArg sExtraArg;
    9314         102 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    9315         204 :     return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    9316             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    9317         204 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    9318             : }
    9319             : 
    9320             : /************************************************************************/
    9321             : /*                            IWriteBlock()                             */
    9322             : /************************************************************************/
    9323             : 
    9324           2 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
    9325             :                                             void *pImage)
    9326             : {
    9327           2 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    9328           2 :     const int nXOff = nBlockXOff * nBlockXSize;
    9329           2 :     const int nYOff = nBlockYOff * nBlockYSize;
    9330           2 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    9331           2 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    9332             :     GDALRasterIOExtraArg sExtraArg;
    9333           2 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    9334           4 :     return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    9335             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    9336           4 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    9337             : }
    9338             : 
    9339             : /************************************************************************/
    9340             : /*                             AdviseRead()                             */
    9341             : /************************************************************************/
    9342             : 
    9343          45 : CPLErr GDALRasterBandFromArray::AdviseRead(int nXOff, int nYOff, int nXSize,
    9344             :                                            int nYSize, int nBufXSize,
    9345             :                                            int nBufYSize,
    9346             :                                            GDALDataType /*eBufType*/,
    9347             :                                            CSLConstList papszOptions)
    9348             : {
    9349          45 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9350          45 :     int bStopProcessing = FALSE;
    9351          90 :     const CPLErr eErr = l_poDS->ValidateRasterIOOrAdviseReadParameters(
    9352             :         "AdviseRead()", &bStopProcessing, nXOff, nYOff, nXSize, nYSize,
    9353          45 :         nBufXSize, nBufYSize, 1, &nBand);
    9354          45 :     if (eErr != CE_None || bStopProcessing)
    9355           0 :         return eErr;
    9356             : 
    9357          45 :     const auto &poArray(l_poDS->m_poArray);
    9358          90 :     std::vector<GUInt64> anArrayStartIdx = m_anOffset;
    9359          45 :     std::vector<size_t> anCount = m_anCount;
    9360          45 :     anArrayStartIdx[l_poDS->m_iXDim] = nXOff;
    9361          45 :     anCount[l_poDS->m_iXDim] = nXSize;
    9362          45 :     if (poArray->GetDimensionCount() >= 2)
    9363             :     {
    9364          42 :         anArrayStartIdx[l_poDS->m_iYDim] = nYOff;
    9365          42 :         anCount[l_poDS->m_iYDim] = nYSize;
    9366             :     }
    9367          45 :     return poArray->AdviseRead(anArrayStartIdx.data(), anCount.data(),
    9368             :                                papszOptions)
    9369          45 :                ? CE_None
    9370          45 :                : CE_Failure;
    9371             : }
    9372             : 
    9373             : /************************************************************************/
    9374             : /*                             IRasterIO()                              */
    9375             : /************************************************************************/
    9376             : 
    9377         547 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
    9378             :                                           int nYOff, int nXSize, int nYSize,
    9379             :                                           void *pData, int nBufXSize,
    9380             :                                           int nBufYSize, GDALDataType eBufType,
    9381             :                                           GSpacing nPixelSpaceBuf,
    9382             :                                           GSpacing nLineSpaceBuf,
    9383             :                                           GDALRasterIOExtraArg *psExtraArg)
    9384             : {
    9385         547 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9386         547 :     const auto &poArray(l_poDS->m_poArray);
    9387         547 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
    9388             :     // If reading/writing at full resolution and with proper stride, go
    9389             :     // directly to the array, but, for performance reasons,
    9390             :     // only if exactly on chunk boundaries, otherwise go through the block cache.
    9391         547 :     if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
    9392         547 :         (nPixelSpaceBuf % nBufferDTSize) == 0 &&
    9393         547 :         (nLineSpaceBuf % nBufferDTSize) == 0 && (nXOff % nBlockXSize) == 0 &&
    9394         545 :         (nYOff % nBlockYSize) == 0 &&
    9395         545 :         ((nXSize % nBlockXSize) == 0 || nXOff + nXSize == nRasterXSize) &&
    9396         542 :         ((nYSize % nBlockYSize) == 0 || nYOff + nYSize == nRasterYSize))
    9397             :     {
    9398         542 :         m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
    9399         542 :         m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
    9400        1084 :         m_anStride[l_poDS->m_iXDim] =
    9401         542 :             static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
    9402         542 :         if (poArray->GetDimensionCount() >= 2)
    9403             :         {
    9404         529 :             m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
    9405         529 :             m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
    9406         529 :             m_anStride[l_poDS->m_iYDim] =
    9407         529 :                 static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
    9408             :         }
    9409         542 :         if (eRWFlag == GF_Read)
    9410             :         {
    9411        1046 :             return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
    9412         523 :                                  m_anStride.data(),
    9413        1046 :                                  GDALExtendedDataType::Create(eBufType), pData)
    9414         523 :                        ? CE_None
    9415         523 :                        : CE_Failure;
    9416             :         }
    9417             :         else
    9418             :         {
    9419          38 :             return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
    9420          19 :                                   m_anStride.data(),
    9421          38 :                                   GDALExtendedDataType::Create(eBufType), pData)
    9422          19 :                        ? CE_None
    9423          19 :                        : CE_Failure;
    9424             :         }
    9425             :     }
    9426             :     // For unaligned reads, give the array a chance to pre-populate its
    9427             :     // internal chunk cache (e.g. Zarr v3 sharded batches I/O via
    9428             :     // PreloadShardedBlocks). The block cache loop below then hits the
    9429             :     // already-decompressed chunks instead of issuing individual reads.
    9430             :     // Backends that don't override AdviseRead() return true (no-op).
    9431           5 :     if (eRWFlag == GF_Read)
    9432             :     {
    9433           4 :         AdviseRead(nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, eBufType,
    9434             :                    nullptr);
    9435             :     }
    9436           5 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    9437             :                                      pData, nBufXSize, nBufYSize, eBufType,
    9438           5 :                                      nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
    9439             : }
    9440             : 
    9441             : /************************************************************************/
    9442             : /*                       GetColorInterpretation()                       */
    9443             : /************************************************************************/
    9444             : 
    9445          80 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
    9446             : {
    9447          80 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9448          80 :     const auto &poArray(l_poDS->m_poArray);
    9449         240 :     auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
    9450          80 :     if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
    9451             :     {
    9452           6 :         bool bOK = false;
    9453           6 :         GUInt64 nStartIndex = 0;
    9454           6 :         if (poArray->GetDimensionCount() == 2 &&
    9455           0 :             poAttr->GetDimensionCount() == 0)
    9456             :         {
    9457           0 :             bOK = true;
    9458             :         }
    9459           6 :         else if (poArray->GetDimensionCount() == 3)
    9460             :         {
    9461           6 :             uint64_t nExtraDimSamples = 1;
    9462           6 :             const auto &apoDims = poArray->GetDimensions();
    9463          24 :             for (size_t i = 0; i < apoDims.size(); ++i)
    9464             :             {
    9465          18 :                 if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
    9466           6 :                     nExtraDimSamples *= apoDims[i]->GetSize();
    9467             :             }
    9468           6 :             if (poAttr->GetDimensionsSize() ==
    9469          12 :                 std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
    9470             :             {
    9471           6 :                 bOK = true;
    9472             :             }
    9473           6 :             nStartIndex = nBand - 1;
    9474             :         }
    9475           6 :         if (bOK)
    9476             :         {
    9477           6 :             const auto oStringDT = GDALExtendedDataType::CreateString();
    9478           6 :             const size_t nCount = 1;
    9479           6 :             const GInt64 arrayStep = 1;
    9480           6 :             const GPtrDiff_t bufferStride = 1;
    9481           6 :             char *pszValue = nullptr;
    9482           6 :             poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
    9483           6 :                          oStringDT, &pszValue);
    9484           6 :             if (pszValue)
    9485             :             {
    9486             :                 const auto eColorInterp =
    9487           6 :                     GDALGetColorInterpretationByName(pszValue);
    9488           6 :                 CPLFree(pszValue);
    9489           6 :                 return eColorInterp;
    9490             :             }
    9491             :         }
    9492             :     }
    9493          74 :     return GCI_Undefined;
    9494             : }
    9495             : 
    9496             : /************************************************************************/
    9497             : /*             GDALRasterBandFromArray::GetOverviewCount()              */
    9498             : /************************************************************************/
    9499             : 
    9500          37 : int GDALRasterBandFromArray::GetOverviewCount()
    9501             : {
    9502          37 :     const int nPAMCount = GDALPamRasterBand::GetOverviewCount();
    9503          37 :     if (nPAMCount)
    9504           2 :         return nPAMCount;
    9505          35 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9506          35 :     l_poDS->DiscoverOverviews();
    9507          35 :     return static_cast<int>(l_poDS->m_apoOverviews.size());
    9508             : }
    9509             : 
    9510             : /************************************************************************/
    9511             : /*                GDALRasterBandFromArray::GetOverview()                */
    9512             : /************************************************************************/
    9513             : 
    9514          34 : GDALRasterBand *GDALRasterBandFromArray::GetOverview(int idx)
    9515             : {
    9516          34 :     const int nPAMCount = GDALPamRasterBand::GetOverviewCount();
    9517          34 :     if (nPAMCount)
    9518           1 :         return GDALPamRasterBand::GetOverview(idx);
    9519          33 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9520          33 :     l_poDS->DiscoverOverviews();
    9521          33 :     if (idx < 0 || static_cast<size_t>(idx) >= l_poDS->m_apoOverviews.size())
    9522             :     {
    9523           8 :         return nullptr;
    9524             :     }
    9525          25 :     return l_poDS->m_apoOverviews[idx]->GetRasterBand(nBand);
    9526             : }
    9527             : 
    9528             : /************************************************************************/
    9529             : /*                    GDALDatasetFromArray::Create()                    */
    9530             : /************************************************************************/
    9531             : 
    9532         527 : std::unique_ptr<GDALDatasetFromArray> GDALDatasetFromArray::Create(
    9533             :     const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
    9534             :     const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
    9535             : 
    9536             : {
    9537         527 :     const auto nDimCount(array->GetDimensionCount());
    9538         527 :     if (nDimCount == 0)
    9539             :     {
    9540           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    9541             :                  "Unsupported number of dimensions");
    9542           1 :         return nullptr;
    9543             :     }
    9544        1051 :     if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
    9545         525 :         array->GetDataType().GetNumericDataType() == GDT_Unknown)
    9546             :     {
    9547           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    9548             :                  "Only arrays with numeric data types "
    9549             :                  "can be exposed as classic GDALDataset");
    9550           1 :         return nullptr;
    9551             :     }
    9552         525 :     if (iXDim >= nDimCount || iYDim >= nDimCount ||
    9553         499 :         (nDimCount >= 2 && iXDim == iYDim))
    9554             :     {
    9555           8 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
    9556           8 :         return nullptr;
    9557             :     }
    9558         517 :     GUInt64 nTotalBands = 1;
    9559         517 :     const auto &dims(array->GetDimensions());
    9560        1630 :     for (size_t i = 0; i < nDimCount; ++i)
    9561             :     {
    9562        1114 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9563             :         {
    9564         101 :             if (dims[i]->GetSize() > 65536 / nTotalBands)
    9565             :             {
    9566           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9567             :                          "Too many bands. Operate on a sliced view");
    9568           1 :                 return nullptr;
    9569             :             }
    9570         100 :             nTotalBands *= dims[i]->GetSize();
    9571             :         }
    9572             :     }
    9573             : 
    9574        1032 :     std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
    9575        1032 :     std::vector<size_t> oMapArrayExtraDimIdxToOriginalIdx;
    9576        1629 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    9577             :     {
    9578        1113 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9579             :         {
    9580         100 :             oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
    9581         100 :             oMapArrayExtraDimIdxToOriginalIdx.push_back(i);
    9582         100 :             ++j;
    9583             :         }
    9584             :     }
    9585             : 
    9586         516 :     const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
    9587             : 
    9588             :     const char *pszBandMetadata =
    9589         516 :         CSLFetchNameValue(papszOptions, "BAND_METADATA");
    9590             :     std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
    9591        1032 :         nNewDimCount);
    9592         516 :     if (pszBandMetadata)
    9593             :     {
    9594          32 :         if (!poRootGroup)
    9595             :         {
    9596           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9597             :                      "Root group should be provided when BAND_METADATA is set");
    9598          24 :             return nullptr;
    9599             :         }
    9600          31 :         CPLJSONDocument oDoc;
    9601          31 :         if (!oDoc.LoadMemory(pszBandMetadata))
    9602             :         {
    9603           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9604             :                      "Invalid JSON content for BAND_METADATA");
    9605           1 :             return nullptr;
    9606             :         }
    9607          30 :         auto oRoot = oDoc.GetRoot();
    9608          30 :         if (oRoot.GetType() != CPLJSONObject::Type::Array)
    9609             :         {
    9610           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9611             :                      "Value of BAND_METADATA should be an array");
    9612           1 :             return nullptr;
    9613             :         }
    9614             : 
    9615          29 :         auto oArray = oRoot.ToArray();
    9616          38 :         for (int j = 0; j < oArray.Size(); ++j)
    9617             :         {
    9618          30 :             const auto oJsonItem = oArray[j];
    9619          30 :             MetadataItem oItem;
    9620          30 :             size_t iExtraDimIdx = 0;
    9621             : 
    9622          60 :             const auto osBandArrayFullname = oJsonItem.GetString("array");
    9623          60 :             const auto osBandAttributeName = oJsonItem.GetString("attribute");
    9624           0 :             std::shared_ptr<GDALMDArray> poArray;
    9625           0 :             std::shared_ptr<GDALAttribute> poAttribute;
    9626          30 :             if (osBandArrayFullname.empty() && osBandAttributeName.empty())
    9627             :             {
    9628           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9629             :                          "BAND_METADATA[%d][\"array\"] or "
    9630             :                          "BAND_METADATA[%d][\"attribute\"] is missing",
    9631             :                          j, j);
    9632           1 :                 return nullptr;
    9633             :             }
    9634          48 :             else if (!osBandArrayFullname.empty() &&
    9635          19 :                      !osBandAttributeName.empty())
    9636             :             {
    9637           1 :                 CPLError(
    9638             :                     CE_Failure, CPLE_AppDefined,
    9639             :                     "BAND_METADATA[%d][\"array\"] and "
    9640             :                     "BAND_METADATA[%d][\"attribute\"] are mutually exclusive",
    9641             :                     j, j);
    9642           1 :                 return nullptr;
    9643             :             }
    9644          28 :             else if (!osBandArrayFullname.empty())
    9645             :             {
    9646             :                 poArray =
    9647          18 :                     poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
    9648          18 :                 if (!poArray)
    9649             :                 {
    9650           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9651             :                              "Array %s cannot be found",
    9652             :                              osBandArrayFullname.c_str());
    9653           3 :                     return nullptr;
    9654             :                 }
    9655          17 :                 if (poArray->GetDimensionCount() != 1)
    9656             :                 {
    9657           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9658             :                              "Array %s is not a 1D array",
    9659             :                              osBandArrayFullname.c_str());
    9660           1 :                     return nullptr;
    9661             :                 }
    9662             :                 const auto &osAuxArrayDimName =
    9663          16 :                     poArray->GetDimensions()[0]->GetName();
    9664             :                 auto oIter =
    9665          16 :                     oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
    9666          16 :                 if (oIter == oMapArrayDimNameToExtraDimIdx.end())
    9667             :                 {
    9668           1 :                     CPLError(
    9669             :                         CE_Failure, CPLE_AppDefined,
    9670             :                         "Dimension %s of array %s is not a non-X/Y dimension "
    9671             :                         "of array %s",
    9672             :                         osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
    9673           1 :                         array->GetName().c_str());
    9674           1 :                     return nullptr;
    9675             :                 }
    9676          15 :                 iExtraDimIdx = oIter->second;
    9677          15 :                 CPLAssert(iExtraDimIdx < nNewDimCount);
    9678             :             }
    9679             :             else
    9680             :             {
    9681          10 :                 CPLAssert(!osBandAttributeName.empty());
    9682          10 :                 poAttribute = !osBandAttributeName.empty() &&
    9683          10 :                                       osBandAttributeName[0] == '/'
    9684          24 :                                   ? poRootGroup->OpenAttributeFromFullname(
    9685             :                                         osBandAttributeName)
    9686          10 :                                   : array->GetAttribute(osBandAttributeName);
    9687          10 :                 if (!poAttribute)
    9688             :                 {
    9689           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9690             :                              "Attribute %s cannot be found",
    9691             :                              osBandAttributeName.c_str());
    9692           8 :                     return nullptr;
    9693             :                 }
    9694           8 :                 const auto aoAttrDims = poAttribute->GetDimensionsSize();
    9695           8 :                 if (aoAttrDims.size() != 1)
    9696             :                 {
    9697           4 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9698             :                              "Attribute %s is not a 1D array",
    9699             :                              osBandAttributeName.c_str());
    9700           4 :                     return nullptr;
    9701             :                 }
    9702           4 :                 bool found = false;
    9703           8 :                 for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
    9704             :                 {
    9705           5 :                     if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
    9706           5 :                             ->GetSize() == aoAttrDims[0])
    9707             :                     {
    9708           4 :                         if (found)
    9709             :                         {
    9710           2 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9711             :                                      "Several dimensions of %s have the same "
    9712             :                                      "size as attribute %s. Cannot infer which "
    9713             :                                      "one to bind to!",
    9714           1 :                                      array->GetName().c_str(),
    9715             :                                      osBandAttributeName.c_str());
    9716           1 :                             return nullptr;
    9717             :                         }
    9718           3 :                         found = true;
    9719           3 :                         iExtraDimIdx = iter.second;
    9720             :                     }
    9721             :                 }
    9722           3 :                 if (!found)
    9723             :                 {
    9724           2 :                     CPLError(
    9725             :                         CE_Failure, CPLE_AppDefined,
    9726             :                         "No dimension of %s has the same size as attribute %s",
    9727           1 :                         array->GetName().c_str(), osBandAttributeName.c_str());
    9728           1 :                     return nullptr;
    9729             :                 }
    9730             :             }
    9731             : 
    9732          17 :             oItem.osName = oJsonItem.GetString("item_name");
    9733          17 :             if (oItem.osName.empty())
    9734             :             {
    9735           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9736             :                          "BAND_METADATA[%d][\"item_name\"] is missing", j);
    9737           1 :                 return nullptr;
    9738             :             }
    9739             : 
    9740          32 :             const auto osDefinition = oJsonItem.GetString("item_value", "%s");
    9741             : 
    9742             :             // Check correctness of definition
    9743          16 :             bool bFirstNumericFormatter = true;
    9744          16 :             std::string osModDefinition;
    9745          16 :             bool bDefinitionUsesPctForG = false;
    9746          79 :             for (size_t k = 0; k < osDefinition.size(); ++k)
    9747             :             {
    9748          70 :                 if (osDefinition[k] == '%')
    9749             :                 {
    9750          15 :                     osModDefinition += osDefinition[k];
    9751          15 :                     if (k + 1 == osDefinition.size())
    9752             :                     {
    9753           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9754             :                                  "Value of "
    9755             :                                  "BAND_METADATA[%d][\"item_value\"] = "
    9756             :                                  "%s is invalid at offset %d",
    9757             :                                  j, osDefinition.c_str(), int(k));
    9758           1 :                         return nullptr;
    9759             :                     }
    9760          14 :                     ++k;
    9761          14 :                     if (osDefinition[k] == '%')
    9762             :                     {
    9763           1 :                         osModDefinition += osDefinition[k];
    9764           1 :                         continue;
    9765             :                     }
    9766          13 :                     if (!bFirstNumericFormatter)
    9767             :                     {
    9768           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9769             :                                  "Value of "
    9770             :                                  "BAND_METADATA[%d][\"item_value\"] = %s is "
    9771             :                                  "invalid at offset %d: %%[x][.y]f|g or %%s "
    9772             :                                  "formatters should be specified at most once",
    9773             :                                  j, osDefinition.c_str(), int(k));
    9774           1 :                         return nullptr;
    9775             :                     }
    9776          12 :                     bFirstNumericFormatter = false;
    9777          19 :                     for (; k < osDefinition.size(); ++k)
    9778             :                     {
    9779          19 :                         osModDefinition += osDefinition[k];
    9780          38 :                         if (!((osDefinition[k] >= '0' &&
    9781          16 :                                osDefinition[k] <= '9') ||
    9782          15 :                               osDefinition[k] == '.'))
    9783          12 :                             break;
    9784             :                     }
    9785          24 :                     if (k == osDefinition.size() ||
    9786          12 :                         (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
    9787           5 :                          osDefinition[k] != 's'))
    9788             :                     {
    9789           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9790             :                                  "Value of "
    9791             :                                  "BAND_METADATA[%d][\"item_value\"] = "
    9792             :                                  "%s is invalid at offset %d: only "
    9793             :                                  "%%[x][.y]f|g or %%s formatters are accepted",
    9794             :                                  j, osDefinition.c_str(), int(k));
    9795           1 :                         return nullptr;
    9796             :                     }
    9797          11 :                     bDefinitionUsesPctForG =
    9798          11 :                         (osDefinition[k] == 'f' || osDefinition[k] == 'g');
    9799          11 :                     if (bDefinitionUsesPctForG)
    9800             :                     {
    9801          12 :                         if (poArray &&
    9802          12 :                             poArray->GetDataType().GetClass() != GEDTC_NUMERIC)
    9803             :                         {
    9804           1 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9805             :                                      "Data type of %s array is not numeric",
    9806           1 :                                      poArray->GetName().c_str());
    9807           1 :                             return nullptr;
    9808             :                         }
    9809           8 :                         else if (poAttribute &&
    9810           2 :                                  poAttribute->GetDataType().GetClass() !=
    9811           6 :                                      GEDTC_NUMERIC)
    9812             :                         {
    9813           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9814             :                                      "Data type of %s attribute is not numeric",
    9815           0 :                                      poAttribute->GetFullName().c_str());
    9816           0 :                             return nullptr;
    9817             :                         }
    9818             :                     }
    9819             :                 }
    9820          62 :                 else if (osDefinition[k] == '$' &&
    9821          62 :                          k + 1 < osDefinition.size() &&
    9822           7 :                          osDefinition[k + 1] == '{')
    9823             :                 {
    9824           7 :                     const auto nPos = osDefinition.find('}', k);
    9825           7 :                     if (nPos == std::string::npos)
    9826             :                     {
    9827           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9828             :                                  "Value of "
    9829             :                                  "BAND_METADATA[%d][\"item_value\"] = "
    9830             :                                  "%s is invalid at offset %d",
    9831             :                                  j, osDefinition.c_str(), int(k));
    9832           3 :                         return nullptr;
    9833             :                     }
    9834             :                     const auto osAttrName =
    9835           6 :                         osDefinition.substr(k + 2, nPos - (k + 2));
    9836           0 :                     std::shared_ptr<GDALAttribute> poAttr;
    9837           6 :                     if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
    9838             :                     {
    9839           4 :                         poAttr = poArray->GetAttribute(osAttrName);
    9840           4 :                         if (!poAttr)
    9841             :                         {
    9842           1 :                             CPLError(
    9843             :                                 CE_Failure, CPLE_AppDefined,
    9844             :                                 "Value of "
    9845             :                                 "BAND_METADATA[%d][\"item_value\"] = "
    9846             :                                 "%s is invalid: %s is not an attribute of %s",
    9847             :                                 j, osDefinition.c_str(), osAttrName.c_str(),
    9848           1 :                                 poArray->GetName().c_str());
    9849           1 :                             return nullptr;
    9850             :                         }
    9851             :                     }
    9852             :                     else
    9853             :                     {
    9854             :                         poAttr =
    9855           2 :                             poRootGroup->OpenAttributeFromFullname(osAttrName);
    9856           2 :                         if (!poAttr)
    9857             :                         {
    9858           1 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9859             :                                      "Value of "
    9860             :                                      "BAND_METADATA[%d][\"item_value\"] = "
    9861             :                                      "%s is invalid: %s is not an attribute",
    9862             :                                      j, osDefinition.c_str(),
    9863             :                                      osAttrName.c_str());
    9864           1 :                             return nullptr;
    9865             :                         }
    9866             :                     }
    9867           4 :                     k = nPos;
    9868           4 :                     const char *pszValue = poAttr->ReadAsString();
    9869           4 :                     if (!pszValue)
    9870             :                     {
    9871           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9872             :                                  "Cannot get value of attribute %s as a "
    9873             :                                  "string",
    9874             :                                  osAttrName.c_str());
    9875           0 :                         return nullptr;
    9876             :                     }
    9877           4 :                     osModDefinition += pszValue;
    9878             :                 }
    9879             :                 else
    9880             :                 {
    9881          48 :                     osModDefinition += osDefinition[k];
    9882             :                 }
    9883             :             }
    9884             : 
    9885           9 :             if (poArray)
    9886           8 :                 oItem.poArray = std::move(poArray);
    9887             :             else
    9888           1 :                 oItem.poArray = std::move(poAttribute);
    9889           9 :             oItem.osDefinition = std::move(osModDefinition);
    9890           9 :             oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
    9891             : 
    9892           9 :             aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
    9893           9 :                 std::move(oItem));
    9894             :         }
    9895             :     }
    9896             : 
    9897         984 :     std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
    9898             :     const char *pszBandImageryMetadata =
    9899         492 :         CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
    9900         492 :     if (pszBandImageryMetadata)
    9901             :     {
    9902          20 :         if (!poRootGroup)
    9903             :         {
    9904           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9905             :                      "Root group should be provided when BAND_IMAGERY_METADATA "
    9906             :                      "is set");
    9907          17 :             return nullptr;
    9908             :         }
    9909          19 :         CPLJSONDocument oDoc;
    9910          19 :         if (!oDoc.LoadMemory(pszBandImageryMetadata))
    9911             :         {
    9912           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9913             :                      "Invalid JSON content for BAND_IMAGERY_METADATA");
    9914           1 :             return nullptr;
    9915             :         }
    9916          18 :         auto oRoot = oDoc.GetRoot();
    9917          18 :         if (oRoot.GetType() != CPLJSONObject::Type::Object)
    9918             :         {
    9919           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9920             :                      "Value of BAND_IMAGERY_METADATA should be an object");
    9921           1 :             return nullptr;
    9922             :         }
    9923          21 :         for (const auto &oJsonItem : oRoot.GetChildren())
    9924             :         {
    9925          38 :             if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
    9926          20 :                 oJsonItem.GetName() == "FWHM_UM")
    9927             :             {
    9928          34 :                 const auto osBandArrayFullname = oJsonItem.GetString("array");
    9929             :                 const auto osBandAttributeName =
    9930          34 :                     oJsonItem.GetString("attribute");
    9931           0 :                 std::shared_ptr<GDALMDArray> poArray;
    9932           0 :                 std::shared_ptr<GDALAttribute> poAttribute;
    9933          17 :                 size_t iExtraDimIdx = 0;
    9934          17 :                 if (osBandArrayFullname.empty() && osBandAttributeName.empty())
    9935             :                 {
    9936           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9937             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] or "
    9938             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] is "
    9939             :                              "missing",
    9940           2 :                              oJsonItem.GetName().c_str(),
    9941           2 :                              oJsonItem.GetName().c_str());
    9942           1 :                     return nullptr;
    9943             :                 }
    9944          25 :                 else if (!osBandArrayFullname.empty() &&
    9945           9 :                          !osBandAttributeName.empty())
    9946             :                 {
    9947           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9948             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] and "
    9949             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] are "
    9950             :                              "mutually exclusive",
    9951           2 :                              oJsonItem.GetName().c_str(),
    9952           2 :                              oJsonItem.GetName().c_str());
    9953           1 :                     return nullptr;
    9954             :                 }
    9955          15 :                 else if (!osBandArrayFullname.empty())
    9956             :                 {
    9957          16 :                     poArray = poRootGroup->OpenMDArrayFromFullname(
    9958           8 :                         osBandArrayFullname);
    9959           8 :                     if (!poArray)
    9960             :                     {
    9961           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9962             :                                  "Array %s cannot be found",
    9963             :                                  osBandArrayFullname.c_str());
    9964           3 :                         return nullptr;
    9965             :                     }
    9966           7 :                     if (poArray->GetDimensionCount() != 1)
    9967             :                     {
    9968           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9969             :                                  "Array %s is not a 1D array",
    9970             :                                  osBandArrayFullname.c_str());
    9971           1 :                         return nullptr;
    9972             :                     }
    9973             :                     const auto &osAuxArrayDimName =
    9974           6 :                         poArray->GetDimensions()[0]->GetName();
    9975             :                     auto oIter =
    9976           6 :                         oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
    9977           6 :                     if (oIter == oMapArrayDimNameToExtraDimIdx.end())
    9978             :                     {
    9979           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9980             :                                  "Dimension \"%s\" of array \"%s\" is not a "
    9981             :                                  "non-X/Y dimension of array \"%s\"",
    9982             :                                  osAuxArrayDimName.c_str(),
    9983             :                                  osBandArrayFullname.c_str(),
    9984           1 :                                  array->GetName().c_str());
    9985           1 :                         return nullptr;
    9986             :                     }
    9987           5 :                     iExtraDimIdx = oIter->second;
    9988           5 :                     CPLAssert(iExtraDimIdx < nNewDimCount);
    9989             :                 }
    9990             :                 else
    9991             :                 {
    9992             :                     poAttribute =
    9993           7 :                         !osBandAttributeName.empty() &&
    9994           7 :                                 osBandAttributeName[0] == '/'
    9995          16 :                             ? poRootGroup->OpenAttributeFromFullname(
    9996             :                                   osBandAttributeName)
    9997           7 :                             : array->GetAttribute(osBandAttributeName);
    9998           7 :                     if (!poAttribute)
    9999             :                     {
   10000           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
   10001             :                                  "Attribute %s cannot be found",
   10002             :                                  osBandAttributeName.c_str());
   10003           6 :                         return nullptr;
   10004             :                     }
   10005           5 :                     const auto aoAttrDims = poAttribute->GetDimensionsSize();
   10006           5 :                     if (aoAttrDims.size() != 1)
   10007             :                     {
   10008           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
   10009             :                                  "Attribute %s is not a 1D array",
   10010             :                                  osBandAttributeName.c_str());
   10011           2 :                         return nullptr;
   10012             :                     }
   10013           3 :                     bool found = false;
   10014           6 :                     for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
   10015             :                     {
   10016           4 :                         if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
   10017           4 :                                 ->GetSize() == aoAttrDims[0])
   10018             :                         {
   10019           3 :                             if (found)
   10020             :                             {
   10021           2 :                                 CPLError(CE_Failure, CPLE_AppDefined,
   10022             :                                          "Several dimensions of %s have the "
   10023             :                                          "same size as attribute %s. Cannot "
   10024             :                                          "infer which one to bind to!",
   10025           1 :                                          array->GetName().c_str(),
   10026             :                                          osBandAttributeName.c_str());
   10027           1 :                                 return nullptr;
   10028             :                             }
   10029           2 :                             found = true;
   10030           2 :                             iExtraDimIdx = iter.second;
   10031             :                         }
   10032             :                     }
   10033           2 :                     if (!found)
   10034             :                     {
   10035           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
   10036             :                                  "No dimension of %s has the same size as "
   10037             :                                  "attribute %s",
   10038           1 :                                  array->GetName().c_str(),
   10039             :                                  osBandAttributeName.c_str());
   10040           1 :                         return nullptr;
   10041             :                     }
   10042             :                 }
   10043             : 
   10044          12 :                 std::string osUnit = oJsonItem.GetString("unit", "um");
   10045           6 :                 if (STARTS_WITH(osUnit.c_str(), "${"))
   10046             :                 {
   10047           4 :                     if (osUnit.back() != '}')
   10048             :                     {
   10049           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
   10050             :                                  "Value of "
   10051             :                                  "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
   10052             :                                  "%s is invalid",
   10053           2 :                                  oJsonItem.GetName().c_str(), osUnit.c_str());
   10054           2 :                         return nullptr;
   10055             :                     }
   10056           3 :                     const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
   10057           0 :                     std::shared_ptr<GDALAttribute> poAttr;
   10058           3 :                     if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
   10059             :                     {
   10060           2 :                         poAttr = poArray->GetAttribute(osAttrName);
   10061           2 :                         if (!poAttr)
   10062             :                         {
   10063           2 :                             CPLError(
   10064             :                                 CE_Failure, CPLE_AppDefined,
   10065             :                                 "Value of "
   10066             :                                 "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
   10067             :                                 "%s is invalid: %s is not an attribute of %s",
   10068           2 :                                 oJsonItem.GetName().c_str(), osUnit.c_str(),
   10069             :                                 osAttrName.c_str(),
   10070             :                                 osBandArrayFullname.c_str());
   10071           1 :                             return nullptr;
   10072             :                         }
   10073             :                     }
   10074             :                     else
   10075             :                     {
   10076             :                         poAttr =
   10077           1 :                             poRootGroup->OpenAttributeFromFullname(osAttrName);
   10078           1 :                         if (!poAttr)
   10079             :                         {
   10080           0 :                             CPLError(
   10081             :                                 CE_Failure, CPLE_AppDefined,
   10082             :                                 "Value of "
   10083             :                                 "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
   10084             :                                 "%s is invalid: %s is not an attribute",
   10085           0 :                                 oJsonItem.GetName().c_str(), osUnit.c_str(),
   10086             :                                 osAttrName.c_str());
   10087           0 :                             return nullptr;
   10088             :                         }
   10089             :                     }
   10090             : 
   10091           2 :                     const char *pszValue = poAttr->ReadAsString();
   10092           2 :                     if (!pszValue)
   10093             :                     {
   10094           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
   10095             :                                  "Cannot get value of attribute %s of %s as a "
   10096             :                                  "string",
   10097             :                                  osAttrName.c_str(),
   10098             :                                  osBandArrayFullname.c_str());
   10099           0 :                         return nullptr;
   10100             :                     }
   10101           2 :                     osUnit = pszValue;
   10102             :                 }
   10103           4 :                 double dfConvToUM = 1.0;
   10104          10 :                 if (osUnit == "nm" || osUnit == "nanometre" ||
   10105          13 :                     osUnit == "nanometres" || osUnit == "nanometer" ||
   10106           3 :                     osUnit == "nanometers")
   10107             :                 {
   10108           1 :                     dfConvToUM = 1e-3;
   10109             :                 }
   10110           5 :                 else if (!(osUnit == "um" || osUnit == "micrometre" ||
   10111           2 :                            osUnit == "micrometres" || osUnit == "micrometer" ||
   10112           1 :                            osUnit == "micrometers"))
   10113             :                 {
   10114           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
   10115             :                              "Unhandled value for "
   10116             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
   10117           2 :                              oJsonItem.GetName().c_str(), osUnit.c_str());
   10118           1 :                     return nullptr;
   10119             :                 }
   10120             : 
   10121           3 :                 BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
   10122             : 
   10123           3 :                 std::shared_ptr<GDALAbstractMDArray> abstractArray;
   10124           3 :                 if (poArray)
   10125           2 :                     abstractArray = std::move(poArray);
   10126             :                 else
   10127           1 :                     abstractArray = std::move(poAttribute);
   10128           3 :                 if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
   10129             :                 {
   10130           2 :                     item.poCentralWavelengthArray = std::move(abstractArray);
   10131           2 :                     item.dfCentralWavelengthToMicrometer = dfConvToUM;
   10132             :                 }
   10133             :                 else
   10134             :                 {
   10135           1 :                     item.poFWHMArray = std::move(abstractArray);
   10136           1 :                     item.dfFWHMToMicrometer = dfConvToUM;
   10137             :                 }
   10138             :             }
   10139             :             else
   10140             :             {
   10141           1 :                 CPLError(CE_Warning, CPLE_AppDefined,
   10142             :                          "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
   10143           2 :                          oJsonItem.GetName().c_str());
   10144             :             }
   10145             :         }
   10146             :     }
   10147             : 
   10148         456 :     if ((nDimCount >= 2 &&
   10149         950 :          dims[iYDim]->GetSize() > static_cast<uint64_t>(INT_MAX)) ||
   10150         475 :         dims[iXDim]->GetSize() > static_cast<uint64_t>(INT_MAX))
   10151             :     {
   10152           1 :         CPLError(CE_Failure, CPLE_AppDefined,
   10153             :                  "Array is too large to be exposed as a GDAL dataset");
   10154           1 :         return nullptr;
   10155             :     }
   10156             : 
   10157             :     auto poDS = std::make_unique<GDALDatasetFromArray>(
   10158         948 :         array, iXDim, iYDim, CPLStringList(papszOptions));
   10159             : 
   10160         474 :     poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
   10161             : 
   10162         474 :     poDS->nRasterYSize =
   10163         474 :         nDimCount < 2 ? 1 : static_cast<int>(dims[iYDim]->GetSize());
   10164             : 
   10165         474 :     poDS->nRasterXSize = static_cast<int>(dims[iXDim]->GetSize());
   10166             : 
   10167         948 :     std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
   10168         948 :     std::vector<GUInt64> anStackIters(nDimCount);
   10169         948 :     std::vector<size_t> anMapNewToOld(nNewDimCount);
   10170        1460 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
   10171             :     {
   10172         986 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
   10173             :         {
   10174          57 :             anMapNewToOld[j] = i;
   10175          57 :             j++;
   10176             :         }
   10177             :     }
   10178             : 
   10179         474 :     poDS->m_bHasGT = array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_gt);
   10180             : 
   10181         948 :     const auto attrs(array->GetAttributes());
   10182         644 :     for (const auto &attr : attrs)
   10183             :     {
   10184         170 :         if (attr->GetName() == "spatial:registration")
   10185             :         {
   10186             :             // From https://github.com/zarr-conventions/spatial
   10187           6 :             const char *pszValue = attr->ReadAsString();
   10188           6 :             if (pszValue && strcmp(pszValue, "pixel") == 0)
   10189           5 :                 poDS->m_oMDD.SetMetadataItem(GDALMD_AREA_OR_POINT,
   10190             :                                              GDALMD_AOP_AREA);
   10191           1 :             else if (pszValue && strcmp(pszValue, "node") == 0)
   10192           1 :                 poDS->m_oMDD.SetMetadataItem(GDALMD_AREA_OR_POINT,
   10193             :                                              GDALMD_AOP_POINT);
   10194           0 :             else if (pszValue)
   10195           0 :                 poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), pszValue);
   10196             :         }
   10197         164 :         else if (attr->GetName() == "gdal:geotransform")
   10198             :         {
   10199             :             // From Zarr driver
   10200           4 :             const auto doubleArray = attr->ReadAsDoubleArray();
   10201           2 :             if (doubleArray.size() == 6)
   10202             :             {
   10203           2 :                 poDS->m_bHasGT = true;
   10204           2 :                 poDS->m_gt = GDALGeoTransform(doubleArray.data());
   10205             :             }
   10206             :         }
   10207         162 :         else if (attr->GetName() != "COLOR_INTERPRETATION")
   10208             :         {
   10209         300 :             auto stringArray = attr->ReadAsStringArray();
   10210         300 :             std::string val;
   10211         150 :             if (stringArray.size() > 1)
   10212             :             {
   10213          59 :                 val += '{';
   10214             :             }
   10215         585 :             for (int i = 0; i < stringArray.size(); ++i)
   10216             :             {
   10217         435 :                 if (i > 0)
   10218         285 :                     val += ',';
   10219         435 :                 val += stringArray[i];
   10220             :             }
   10221         150 :             if (stringArray.size() > 1)
   10222             :             {
   10223          59 :                 val += '}';
   10224             :             }
   10225         150 :             poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
   10226             :         }
   10227             :     }
   10228             : 
   10229         474 :     const char *pszDelay = CSLFetchNameValueDef(
   10230             :         papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
   10231             :         CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
   10232             :     const double dfDelay =
   10233         474 :         EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
   10234         474 :     const auto nStartTime = time(nullptr);
   10235         474 :     bool bHasWarned = false;
   10236             :     // Instantiate bands by iterating over non-XY variables
   10237         474 :     size_t iDim = 0;
   10238         474 :     int nCurBand = 1;
   10239         622 : lbl_next_depth:
   10240         622 :     if (iDim < nNewDimCount)
   10241             :     {
   10242          59 :         anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
   10243          59 :         anOtherDimCoord[iDim] = 0;
   10244             :         while (true)
   10245             :         {
   10246         148 :             ++iDim;
   10247         148 :             goto lbl_next_depth;
   10248         148 :         lbl_return_to_caller:
   10249         148 :             --iDim;
   10250         148 :             --anStackIters[iDim];
   10251         148 :             if (anStackIters[iDim] == 0)
   10252          59 :                 break;
   10253          89 :             ++anOtherDimCoord[iDim];
   10254             :         }
   10255             :     }
   10256             :     else
   10257             :     {
   10258        1126 :         poDS->SetBand(nCurBand,
   10259         563 :                       std::make_unique<GDALRasterBandFromArray>(
   10260         563 :                           poDS.get(), anOtherDimCoord,
   10261             :                           aoBandParameterMetadataItems, aoBandImageryMetadata,
   10262             :                           dfDelay, nStartTime, bHasWarned));
   10263         563 :         ++nCurBand;
   10264             :     }
   10265         622 :     if (iDim > 0)
   10266         148 :         goto lbl_return_to_caller;
   10267             : 
   10268         906 :     if (!array->GetFilename().empty() &&
   10269         432 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "LOAD_PAM", "YES")))
   10270             :     {
   10271         422 :         poDS->SetPhysicalFilename(array->GetFilename().c_str());
   10272             :         std::string osDerivedDatasetName(
   10273             :             CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
   10274         844 :                        int(iYDim), array->GetFullName().c_str()));
   10275         422 :         if (!array->GetContext().empty())
   10276             :         {
   10277           2 :             osDerivedDatasetName += " with context ";
   10278           2 :             osDerivedDatasetName += array->GetContext();
   10279             :         }
   10280         422 :         poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
   10281         422 :         poDS->TryLoadXML();
   10282             : 
   10283           2 :         for (const auto &[pszKey, pszValue] :
   10284             :              cpl::IterateNameValue(static_cast<CSLConstList>(
   10285         424 :                  poDS->GDALPamDataset::GetMetadata())))
   10286             :         {
   10287           1 :             poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
   10288             :         }
   10289             :     }
   10290             : 
   10291         474 :     return poDS;
   10292             : }
   10293             : 
   10294             : /************************************************************************/
   10295             : /*                          AsClassicDataset()                          */
   10296             : /************************************************************************/
   10297             : 
   10298             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   10299             :  *
   10300             :  * In the case of > 2D arrays, additional dimensions will be represented as
   10301             :  * raster bands.
   10302             :  *
   10303             :  * The "reverse" method is GDALRasterBand::AsMDArray().
   10304             :  *
   10305             :  * This is the same as the C function GDALMDArrayAsClassicDataset().
   10306             :  *
   10307             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   10308             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   10309             :  *              Ignored if the dimension count is 1.
   10310             :  * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
   10311             :  *                    and BAND_IMAGERY_METADATA option.
   10312             :  * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
   10313             :  *                     nullptr. Current supported options are:
   10314             :  *                     <ul>
   10315             :  *                     <li>BAND_METADATA: JSON serialized array defining which
   10316             :  *                         arrays of the poRootGroup, indexed by non-X and Y
   10317             :  *                         dimensions, should be mapped as band metadata items.
   10318             :  *                         Each array item should be an object with the
   10319             :  *                         following members:
   10320             :  *                         - "array": full name of a band parameter array.
   10321             :  *                           Such array must be a one
   10322             :  *                           dimensional array, and its dimension must be one of
   10323             :  *                           the dimensions of the array on which the method is
   10324             :  *                           called (excluding the X and Y dimensions).
   10325             :  *                         - "attribute": name relative to *this array or full
   10326             :  *                           name of a single dimension numeric array whose size
   10327             :  *                           must be one of the dimensions of *this array
   10328             :  *                           (excluding the X and Y dimensions).
   10329             :  *                           "array" and "attribute" are mutually exclusive.
   10330             :  *                         - "item_name": band metadata item name
   10331             :  *                         - "item_value": (optional) String, where "%[x][.y]f",
   10332             :  *                           "%[x][.y]g" or "%s" printf-like formatting can be
   10333             :  *                           used to format the corresponding value of the
   10334             :  *                           parameter array. The percentage character should be
   10335             :  *                           repeated: "%%"
   10336             :  *                           "${attribute_name}" can also be used to include the
   10337             :  *                           value of an attribute for "array" when set and if
   10338             :  *                           not starting with '/'. Otherwise if starting with
   10339             :  *                           '/', it is the full path to the attribute.
   10340             :  *
   10341             :  *                           If "item_value" is not provided, a default formatting
   10342             :  *                           of the value will be applied.
   10343             :  *
   10344             :  *                         Example:
   10345             :  *                         [
   10346             :  *                            {
   10347             :  *                              "array": "/sensor_band_parameters/wavelengths",
   10348             :  *                              "item_name": "WAVELENGTH",
   10349             :  *                              "item_value": "%.1f ${units}"
   10350             :  *                            },
   10351             :  *                            {
   10352             :  *                              "array": "/sensor_band_parameters/fwhm",
   10353             :  *                              "item_name": "FWHM"
   10354             :  *                            },
   10355             :  *                            {
   10356             :  *                              "array": "/sensor_band_parameters/fwhm",
   10357             :  *                              "item_name": "FWHM_UNIT",
   10358             :  *                              "item_value": "${units}"
   10359             :  *                            }
   10360             :  *                         ]
   10361             :  *
   10362             :  *                         Example for Planet Labs Tanager radiance products:
   10363             :  *                         [
   10364             :  *                            {
   10365             :  *                              "attribute": "center_wavelengths",
   10366             :  *                              "item_name": "WAVELENGTH",
   10367             :  *                              "item_value": "%.1f ${center_wavelengths_units}"
   10368             :  *                            }
   10369             :  *                         ]
   10370             :  *
   10371             :  *                     </li>
   10372             :  *                     <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
   10373             :  *                         JSON serialized object defining which arrays of the
   10374             :  *                         poRootGroup, indexed by non-X and Y dimensions,
   10375             :  *                         should be mapped as band metadata items in the
   10376             :  *                         band IMAGERY domain.
   10377             :  *                         The object currently accepts 2 members:
   10378             :  *                         - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
   10379             :  *                           micrometers.
   10380             :  *                         - "FWHM_UM": Full-width half-maximum
   10381             :  *                           in micrometers.
   10382             :  *                         The value of each member should be an object with the
   10383             :  *                         following members:
   10384             :  *                         - "array": full name of a band parameter array.
   10385             :  *                           Such array must be a one dimensional array, and its
   10386             :  *                           dimension must be one of the dimensions of the
   10387             :  *                           array on which the method is called
   10388             :  *                           (excluding the X and Y dimensions).
   10389             :  *                         - "attribute": name relative to *this array or full
   10390             :  *                           name of a single dimension numeric array whose size
   10391             :  *                           must be one of the dimensions of *this array
   10392             :  *                           (excluding the X and Y dimensions).
   10393             :  *                           "array" and "attribute" are mutually exclusive,
   10394             :  *                           and one of them is required.
   10395             :  *                         - "unit": (optional) unit of the values pointed in
   10396             :  *                           the above array.
   10397             :  *                           Can be a literal string or a string of the form
   10398             :  *                           "${attribute_name}" to point to an attribute for
   10399             :  *                           "array" when set and if no starting
   10400             :  *                           with '/'. Otherwise if starting with '/', it is
   10401             :  *                           the full path to the attribute.
   10402             :  *                           Accepted values are "um", "micrometer"
   10403             :  *                           (with UK vs US spelling, singular or plural), "nm",
   10404             :  *                           "nanometer" (with UK vs US spelling, singular or
   10405             :  *                           plural)
   10406             :  *                           If not provided, micrometer is assumed.
   10407             :  *
   10408             :  *                         Example for EMIT datasets:
   10409             :  *                         {
   10410             :  *                            "CENTRAL_WAVELENGTH_UM": {
   10411             :  *                                "array": "/sensor_band_parameters/wavelengths",
   10412             :  *                                "unit": "${units}"
   10413             :  *                            },
   10414             :  *                            "FWHM_UM": {
   10415             :  *                                "array": "/sensor_band_parameters/fwhm",
   10416             :  *                                "unit": "${units}"
   10417             :  *                            }
   10418             :  *                         }
   10419             :  *
   10420             :  *                         Example for Planet Labs Tanager radiance products:
   10421             :  *                         {
   10422             :  *                            "CENTRAL_WAVELENGTH_UM": {
   10423             :  *                              "attribute": "center_wavelengths",
   10424             :  *                              "unit": "${center_wavelengths_units}"
   10425             :  *                            },
   10426             :  *                            "FWHM_UM": {
   10427             :  *                              "attribute": "fwhm",
   10428             :  *                              "unit": "${fwhm_units}"
   10429             :  *                            }
   10430             :  *                         }
   10431             :  *
   10432             :  *                     </li>
   10433             :  *                     <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
   10434             :  *                         seconds allowed to set the DIM_{dimname}_VALUE band
   10435             :  *                         metadata items from the indexing variable of the
   10436             :  *                         dimensions.
   10437             :  *                         Default value is 5. 'unlimited' can be used to mean
   10438             :  *                         unlimited delay. Can also be defined globally with
   10439             :  *                         the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
   10440             :  *                         option.</li>
   10441             :  *                     </ul>
   10442             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   10443             :  */
   10444             : GDALDataset *
   10445         516 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
   10446             :                               const std::shared_ptr<GDALGroup> &poRootGroup,
   10447             :                               CSLConstList papszOptions) const
   10448             : {
   10449        1032 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
   10450         516 :     if (!self)
   10451             :     {
   10452           0 :         CPLError(CE_Failure, CPLE_AppDefined,
   10453             :                  "Driver implementation issue: m_pSelf not set !");
   10454           0 :         return nullptr;
   10455             :     }
   10456        1032 :     return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
   10457             :                                         papszOptions)
   10458         516 :         .release();
   10459             : }
   10460             : 
   10461             : /************************************************************************/
   10462             : /*                           GetStatistics()                            */
   10463             : /************************************************************************/
   10464             : 
   10465             : /**
   10466             :  * \brief Fetch statistics.
   10467             :  *
   10468             :  * Returns the minimum, maximum, mean and standard deviation of all
   10469             :  * pixel values in this array.
   10470             :  *
   10471             :  * If bForce is FALSE results will only be returned if it can be done
   10472             :  * quickly (i.e. without scanning the data).  If bForce is FALSE and
   10473             :  * results cannot be returned efficiently, the method will return CE_Warning
   10474             :  * but no warning will have been issued.   This is a non-standard use of
   10475             :  * the CE_Warning return value to indicate "nothing done".
   10476             :  *
   10477             :  * When cached statistics are not available, and bForce is TRUE,
   10478             :  * ComputeStatistics() is called.
   10479             :  *
   10480             :  * Note that file formats using PAM (Persistent Auxiliary Metadata) services
   10481             :  * will generally cache statistics in the .aux.xml file allowing fast fetch
   10482             :  * after the first request.
   10483             :  *
   10484             :  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
   10485             :  *
   10486             :  * This method is the same as the C function GDALMDArrayGetStatistics().
   10487             :  *
   10488             :  * @param bApproxOK Currently ignored. In the future, should be set to true
   10489             :  * if statistics on the whole array are wished, or to false if a subset of it
   10490             :  * may be used.
   10491             :  *
   10492             :  * @param bForce If false statistics will only be returned if it can
   10493             :  * be done without rescanning the image.
   10494             :  *
   10495             :  * @param pdfMin Location into which to load image minimum (may be NULL).
   10496             :  *
   10497             :  * @param pdfMax Location into which to load image maximum (may be NULL).-
   10498             :  *
   10499             :  * @param pdfMean Location into which to load image mean (may be NULL).
   10500             :  *
   10501             :  * @param pdfStdDev Location into which to load image standard deviation
   10502             :  * (may be NULL).
   10503             :  *
   10504             :  * @param pnValidCount Number of samples whose value is different from the
   10505             :  * nodata value. (may be NULL)
   10506             :  *
   10507             :  * @param pfnProgress a function to call to report progress, or NULL.
   10508             :  *
   10509             :  * @param pProgressData application data to pass to the progress function.
   10510             :  *
   10511             :  * @return CE_None on success, CE_Warning if no values returned,
   10512             :  * CE_Failure if an error occurs.
   10513             :  *
   10514             :  * @since GDAL 3.2
   10515             :  */
   10516             : 
   10517          10 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
   10518             :                                   double *pdfMax, double *pdfMean,
   10519             :                                   double *pdfStdDev, GUInt64 *pnValidCount,
   10520             :                                   GDALProgressFunc pfnProgress,
   10521             :                                   void *pProgressData)
   10522             : {
   10523          10 :     if (!bForce)
   10524           1 :         return CE_Warning;
   10525             : 
   10526          18 :     return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
   10527           9 :                              pnValidCount, pfnProgress, pProgressData, nullptr)
   10528           9 :                ? CE_None
   10529           9 :                : CE_Failure;
   10530             : }
   10531             : 
   10532             : /************************************************************************/
   10533             : /*                         ComputeStatistics()                          */
   10534             : /************************************************************************/
   10535             : 
   10536             : /**
   10537             :  * \brief Compute statistics.
   10538             :  *
   10539             :  * Returns the minimum, maximum, mean and standard deviation of all
   10540             :  * pixel values in this array.
   10541             :  *
   10542             :  * Pixels taken into account in statistics are those whose mask value
   10543             :  * (as determined by GetMask()) is non-zero.
   10544             :  *
   10545             :  * Once computed, the statistics will generally be "set" back on the
   10546             :  * owing dataset.
   10547             :  *
   10548             :  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
   10549             :  *
   10550             :  * This method is the same as the C functions GDALMDArrayComputeStatistics().
   10551             :  * and GDALMDArrayComputeStatisticsEx().
   10552             :  *
   10553             :  * @param bApproxOK Currently ignored. In the future, should be set to true
   10554             :  * if statistics on the whole array are wished, or to false if a subset of it
   10555             :  * may be used.
   10556             :  *
   10557             :  * @param pdfMin Location into which to load image minimum (may be NULL).
   10558             :  *
   10559             :  * @param pdfMax Location into which to load image maximum (may be NULL).-
   10560             :  *
   10561             :  * @param pdfMean Location into which to load image mean (may be NULL).
   10562             :  *
   10563             :  * @param pdfStdDev Location into which to load image standard deviation
   10564             :  * (may be NULL).
   10565             :  *
   10566             :  * @param pnValidCount Number of samples whose value is different from the
   10567             :  * nodata value. (may be NULL)
   10568             :  *
   10569             :  * @param pfnProgress a function to call to report progress, or NULL.
   10570             :  *
   10571             :  * @param pProgressData application data to pass to the progress function.
   10572             :  *
   10573             :  * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
   10574             :  *                     Options are driver specific. For now the netCDF and Zarr
   10575             :  *                     drivers recognize UPDATE_METADATA=YES, whose effect is
   10576             :  *                     to add or update the actual_range attribute with the
   10577             :  *                     computed min/max, only if done on the full array, in non
   10578             :  *                     approximate mode, and the dataset is opened in update
   10579             :  *                     mode.
   10580             :  *
   10581             :  * @return true on success
   10582             :  *
   10583             :  * @since GDAL 3.2
   10584             :  */
   10585             : 
   10586          13 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
   10587             :                                     double *pdfMax, double *pdfMean,
   10588             :                                     double *pdfStdDev, GUInt64 *pnValidCount,
   10589             :                                     GDALProgressFunc pfnProgress,
   10590             :                                     void *pProgressData,
   10591             :                                     CSLConstList papszOptions)
   10592             : {
   10593             :     struct StatsPerChunkType
   10594             :     {
   10595             :         const GDALMDArray *array = nullptr;
   10596             :         std::shared_ptr<GDALMDArray> poMask{};
   10597             :         double dfMin = cpl::NumericLimits<double>::max();
   10598             :         double dfMax = -cpl::NumericLimits<double>::max();
   10599             :         double dfMean = 0.0;
   10600             :         double dfM2 = 0.0;
   10601             :         GUInt64 nValidCount = 0;
   10602             :         std::vector<GByte> abyData{};
   10603             :         std::vector<double> adfData{};
   10604             :         std::vector<GByte> abyMaskData{};
   10605             :         GDALProgressFunc pfnProgress = nullptr;
   10606             :         void *pProgressData = nullptr;
   10607             :     };
   10608             : 
   10609          13 :     const auto PerChunkFunc = [](GDALAbstractMDArray *,
   10610             :                                  const GUInt64 *chunkArrayStartIdx,
   10611             :                                  const size_t *chunkCount, GUInt64 iCurChunk,
   10612             :                                  GUInt64 nChunkCount, void *pUserData)
   10613             :     {
   10614          13 :         StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
   10615          13 :         const GDALMDArray *array = data->array;
   10616          13 :         const GDALMDArray *poMask = data->poMask.get();
   10617          13 :         const size_t nDims = array->GetDimensionCount();
   10618          13 :         size_t nVals = 1;
   10619          34 :         for (size_t i = 0; i < nDims; i++)
   10620          21 :             nVals *= chunkCount[i];
   10621             : 
   10622             :         // Get mask
   10623          13 :         data->abyMaskData.resize(nVals);
   10624          13 :         if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
   10625          13 :                            poMask->GetDataType(), &data->abyMaskData[0])))
   10626             :         {
   10627           0 :             return false;
   10628             :         }
   10629             : 
   10630             :         // Get data
   10631          13 :         const auto &oType = array->GetDataType();
   10632          13 :         if (oType.GetNumericDataType() == GDT_Float64)
   10633             :         {
   10634           6 :             data->adfData.resize(nVals);
   10635           6 :             if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
   10636           6 :                              oType, &data->adfData[0]))
   10637             :             {
   10638           0 :                 return false;
   10639             :             }
   10640             :         }
   10641             :         else
   10642             :         {
   10643           7 :             data->abyData.resize(nVals * oType.GetSize());
   10644           7 :             if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
   10645           7 :                              oType, &data->abyData[0]))
   10646             :             {
   10647           0 :                 return false;
   10648             :             }
   10649           7 :             data->adfData.resize(nVals);
   10650           7 :             GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
   10651           7 :                             static_cast<int>(oType.GetSize()),
   10652           7 :                             &data->adfData[0], GDT_Float64,
   10653             :                             static_cast<int>(sizeof(double)),
   10654             :                             static_cast<GPtrDiff_t>(nVals));
   10655             :         }
   10656         912 :         for (size_t i = 0; i < nVals; i++)
   10657             :         {
   10658         899 :             if (data->abyMaskData[i])
   10659             :             {
   10660         894 :                 const double dfValue = data->adfData[i];
   10661         894 :                 data->dfMin = std::min(data->dfMin, dfValue);
   10662         894 :                 data->dfMax = std::max(data->dfMax, dfValue);
   10663         894 :                 data->nValidCount++;
   10664         894 :                 const double dfDelta = dfValue - data->dfMean;
   10665         894 :                 data->dfMean += dfDelta / data->nValidCount;
   10666         894 :                 data->dfM2 += dfDelta * (dfValue - data->dfMean);
   10667             :             }
   10668             :         }
   10669          13 :         if (data->pfnProgress &&
   10670           0 :             !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
   10671             :                                "", data->pProgressData))
   10672             :         {
   10673           0 :             return false;
   10674             :         }
   10675          13 :         return true;
   10676             :     };
   10677             : 
   10678          13 :     const auto &oType = GetDataType();
   10679          26 :     if (oType.GetClass() != GEDTC_NUMERIC ||
   10680          13 :         GDALDataTypeIsComplex(oType.GetNumericDataType()))
   10681             :     {
   10682           0 :         CPLError(
   10683             :             CE_Failure, CPLE_NotSupported,
   10684             :             "Statistics can only be computed on non-complex numeric data type");
   10685           0 :         return false;
   10686             :     }
   10687             : 
   10688          13 :     const size_t nDims = GetDimensionCount();
   10689          26 :     std::vector<GUInt64> arrayStartIdx(nDims);
   10690          26 :     std::vector<GUInt64> count(nDims);
   10691          13 :     const auto &poDims = GetDimensions();
   10692          34 :     for (size_t i = 0; i < nDims; i++)
   10693             :     {
   10694          21 :         count[i] = poDims[i]->GetSize();
   10695             :     }
   10696          13 :     const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
   10697             :     const size_t nMaxChunkSize =
   10698             :         pszSwathSize
   10699          13 :             ? static_cast<size_t>(
   10700           0 :                   std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
   10701           0 :                            CPLAtoGIntBig(pszSwathSize)))
   10702             :             : static_cast<size_t>(
   10703          13 :                   std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
   10704          13 :                            GDALGetCacheMax64() / 4));
   10705          26 :     StatsPerChunkType sData;
   10706          13 :     sData.array = this;
   10707          13 :     sData.poMask = GetMask(nullptr);
   10708          13 :     if (sData.poMask == nullptr)
   10709             :     {
   10710           0 :         return false;
   10711             :     }
   10712          13 :     sData.pfnProgress = pfnProgress;
   10713          13 :     sData.pProgressData = pProgressData;
   10714          13 :     if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
   10715          26 :                          GetProcessingChunkSize(nMaxChunkSize).data(),
   10716          13 :                          PerChunkFunc, &sData))
   10717             :     {
   10718           0 :         return false;
   10719             :     }
   10720             : 
   10721          13 :     if (pdfMin)
   10722          13 :         *pdfMin = sData.dfMin;
   10723             : 
   10724          13 :     if (pdfMax)
   10725          13 :         *pdfMax = sData.dfMax;
   10726             : 
   10727          13 :     if (pdfMean)
   10728          11 :         *pdfMean = sData.dfMean;
   10729             : 
   10730             :     const double dfStdDev =
   10731          13 :         sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
   10732          13 :     if (pdfStdDev)
   10733          11 :         *pdfStdDev = dfStdDev;
   10734             : 
   10735          13 :     if (pnValidCount)
   10736          11 :         *pnValidCount = sData.nValidCount;
   10737             : 
   10738          13 :     SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
   10739          13 :                   sData.nValidCount, papszOptions);
   10740             : 
   10741          13 :     return true;
   10742             : }
   10743             : 
   10744             : /************************************************************************/
   10745             : /*                           SetStatistics()                            */
   10746             : /************************************************************************/
   10747             : //! @cond Doxygen_Suppress
   10748           5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
   10749             :                                 double /* dfMax */, double /* dfMean */,
   10750             :                                 double /* dfStdDev */,
   10751             :                                 GUInt64 /* nValidCount */,
   10752             :                                 CSLConstList /* papszOptions */)
   10753             : {
   10754           5 :     CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
   10755           5 :     return false;
   10756             : }
   10757             : 
   10758             : //! @endcond
   10759             : 
   10760             : /************************************************************************/
   10761             : /*                          ClearStatistics()                           */
   10762             : /************************************************************************/
   10763             : 
   10764             : /**
   10765             :  * \brief Clear statistics.
   10766             :  *
   10767             :  * @since GDAL 3.4
   10768             :  */
   10769           0 : void GDALMDArray::ClearStatistics()
   10770             : {
   10771           0 : }
   10772             : 
   10773             : /************************************************************************/
   10774             : /*                       GetCoordinateVariables()                       */
   10775             : /************************************************************************/
   10776             : 
   10777             : /**
   10778             :  * \brief Return coordinate variables.
   10779             :  *
   10780             :  * Coordinate variables are an alternate way of indexing an array that can
   10781             :  * be sometimes used. For example, an array collected through remote sensing
   10782             :  * might be indexed by (scanline, pixel). But there can be
   10783             :  * a longitude and latitude arrays alongside that are also both indexed by
   10784             :  * (scanline, pixel), and are referenced from operational arrays for
   10785             :  * reprojection purposes.
   10786             :  *
   10787             :  * For netCDF, this will return the arrays referenced by the "coordinates"
   10788             :  * attribute.
   10789             :  *
   10790             :  * This method is the same as the C function
   10791             :  * GDALMDArrayGetCoordinateVariables().
   10792             :  *
   10793             :  * @return a vector of arrays
   10794             :  *
   10795             :  * @since GDAL 3.4
   10796             :  */
   10797             : 
   10798             : std::vector<std::shared_ptr<GDALMDArray>>
   10799          13 : GDALMDArray::GetCoordinateVariables() const
   10800             : {
   10801          13 :     return {};
   10802             : }
   10803             : 
   10804             : /************************************************************************/
   10805             : /*                       ~GDALExtendedDataType()                        */
   10806             : /************************************************************************/
   10807             : 
   10808             : GDALExtendedDataType::~GDALExtendedDataType() = default;
   10809             : 
   10810             : /************************************************************************/
   10811             : /*                        GDALExtendedDataType()                        */
   10812             : /************************************************************************/
   10813             : 
   10814       44531 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
   10815       44531 :                                            GDALExtendedDataTypeSubType eSubType)
   10816             :     : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
   10817       44531 :       m_nMaxStringLength(nMaxStringLength)
   10818             : {
   10819       44531 : }
   10820             : 
   10821             : /************************************************************************/
   10822             : /*                        GDALExtendedDataType()                        */
   10823             : /************************************************************************/
   10824             : 
   10825      336740 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
   10826             :     : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
   10827      336740 :       m_nSize(GDALGetDataTypeSizeBytes(eType))
   10828             : {
   10829      336740 : }
   10830             : 
   10831             : /************************************************************************/
   10832             : /*                        GDALExtendedDataType()                        */
   10833             : /************************************************************************/
   10834             : 
   10835          63 : GDALExtendedDataType::GDALExtendedDataType(
   10836             :     const std::string &osName, GDALDataType eBaseType,
   10837          63 :     std::unique_ptr<GDALRasterAttributeTable> poRAT)
   10838             :     : m_osName(osName), m_eClass(GEDTC_NUMERIC), m_eNumericDT(eBaseType),
   10839          63 :       m_nSize(GDALGetDataTypeSizeBytes(eBaseType)), m_poRAT(std::move(poRAT))
   10840             : {
   10841          63 : }
   10842             : 
   10843             : /************************************************************************/
   10844             : /*                        GDALExtendedDataType()                        */
   10845             : /************************************************************************/
   10846             : 
   10847        1765 : GDALExtendedDataType::GDALExtendedDataType(
   10848             :     const std::string &osName, size_t nTotalSize,
   10849        1765 :     std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
   10850             :     : m_osName(osName), m_eClass(GEDTC_COMPOUND),
   10851        1765 :       m_aoComponents(std::move(components)), m_nSize(nTotalSize)
   10852             : {
   10853        1765 : }
   10854             : 
   10855             : /************************************************************************/
   10856             : /*                        GDALExtendedDataType()                        */
   10857             : /************************************************************************/
   10858             : 
   10859             : /** Move constructor. */
   10860             : GDALExtendedDataType::GDALExtendedDataType(GDALExtendedDataType &&) = default;
   10861             : 
   10862             : /************************************************************************/
   10863             : /*                        GDALExtendedDataType()                        */
   10864             : /************************************************************************/
   10865             : 
   10866             : /** Copy constructor. */
   10867       31527 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
   10868       63054 :     : m_osName(other.m_osName), m_eClass(other.m_eClass),
   10869       31527 :       m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
   10870       31527 :       m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength),
   10871       31527 :       m_poRAT(other.m_poRAT ? other.m_poRAT->Clone() : nullptr)
   10872             : {
   10873       31527 :     if (m_eClass == GEDTC_COMPOUND)
   10874             :     {
   10875         481 :         for (const auto &elt : other.m_aoComponents)
   10876             :         {
   10877         318 :             m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
   10878             :         }
   10879             :     }
   10880       31527 : }
   10881             : 
   10882             : /************************************************************************/
   10883             : /*                             operator= ()                             */
   10884             : /************************************************************************/
   10885             : 
   10886             : /** Copy assignment. */
   10887             : GDALExtendedDataType &
   10888        9068 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
   10889             : {
   10890        9068 :     if (this != &other)
   10891             :     {
   10892        9068 :         m_osName = other.m_osName;
   10893        9068 :         m_eClass = other.m_eClass;
   10894        9068 :         m_eSubType = other.m_eSubType;
   10895        9068 :         m_eNumericDT = other.m_eNumericDT;
   10896        9068 :         m_nSize = other.m_nSize;
   10897        9068 :         m_nMaxStringLength = other.m_nMaxStringLength;
   10898        9068 :         m_poRAT.reset(other.m_poRAT ? other.m_poRAT->Clone() : nullptr);
   10899        9068 :         m_aoComponents.clear();
   10900        9068 :         if (m_eClass == GEDTC_COMPOUND)
   10901             :         {
   10902           0 :             for (const auto &elt : other.m_aoComponents)
   10903             :             {
   10904           0 :                 m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
   10905             :             }
   10906             :         }
   10907             :     }
   10908        9068 :     return *this;
   10909             : }
   10910             : 
   10911             : /************************************************************************/
   10912             : /*                             operator= ()                             */
   10913             : /************************************************************************/
   10914             : 
   10915             : /** Move assignment. */
   10916             : GDALExtendedDataType &
   10917             : GDALExtendedDataType::operator=(GDALExtendedDataType &&other) = default;
   10918             : 
   10919             : /************************************************************************/
   10920             : /*                               Create()                               */
   10921             : /************************************************************************/
   10922             : 
   10923             : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
   10924             :  *
   10925             :  * This is the same as the C function GDALExtendedDataTypeCreate()
   10926             :  *
   10927             :  * @param eType Numeric data type. Must be different from GDT_Unknown and
   10928             :  * GDT_TypeCount
   10929             :  */
   10930      336733 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
   10931             : {
   10932      336733 :     return GDALExtendedDataType(eType);
   10933             : }
   10934             : 
   10935             : /************************************************************************/
   10936             : /*                               Create()                               */
   10937             : /************************************************************************/
   10938             : 
   10939             : /** Return a new GDALExtendedDataType from a raster attribute table.
   10940             :  *
   10941             :  * @param osName Type name
   10942             :  * @param eBaseType Base integer data type.
   10943             :  * @param poRAT Raster attribute table. Must not be NULL.
   10944             :  * @since 3.12
   10945             :  */
   10946             : GDALExtendedDataType
   10947          63 : GDALExtendedDataType::Create(const std::string &osName, GDALDataType eBaseType,
   10948             :                              std::unique_ptr<GDALRasterAttributeTable> poRAT)
   10949             : {
   10950          63 :     return GDALExtendedDataType(osName, eBaseType, std::move(poRAT));
   10951             : }
   10952             : 
   10953             : /************************************************************************/
   10954             : /*                               Create()                               */
   10955             : /************************************************************************/
   10956             : 
   10957             : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
   10958             :  *
   10959             :  * This is the same as the C function GDALExtendedDataTypeCreateCompound()
   10960             :  *
   10961             :  * @param osName Type name.
   10962             :  * @param nTotalSize Total size of the type in bytes.
   10963             :  *                   Should be large enough to store all components.
   10964             :  * @param components Components of the compound type.
   10965             :  */
   10966        1772 : GDALExtendedDataType GDALExtendedDataType::Create(
   10967             :     const std::string &osName, size_t nTotalSize,
   10968             :     std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
   10969             : {
   10970        1772 :     size_t nLastOffset = 0;
   10971             :     // Some arbitrary threshold to avoid potential integer overflows
   10972        1772 :     if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
   10973             :     {
   10974           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10975           2 :         return GDALExtendedDataType(GDT_Unknown);
   10976             :     }
   10977        7512 :     for (const auto &comp : components)
   10978             :     {
   10979             :         // Check alignment too ?
   10980        5743 :         if (comp->GetOffset() < nLastOffset)
   10981             :         {
   10982           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10983           1 :             return GDALExtendedDataType(GDT_Unknown);
   10984             :         }
   10985        5742 :         nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
   10986             :     }
   10987        1769 :     if (nTotalSize < nLastOffset)
   10988             :     {
   10989           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10990           1 :         return GDALExtendedDataType(GDT_Unknown);
   10991             :     }
   10992        1768 :     if (nTotalSize == 0 || components.empty())
   10993             :     {
   10994           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
   10995           3 :         return GDALExtendedDataType(GDT_Unknown);
   10996             :     }
   10997        1765 :     return GDALExtendedDataType(osName, nTotalSize, std::move(components));
   10998             : }
   10999             : 
   11000             : /************************************************************************/
   11001             : /*                               Create()                               */
   11002             : /************************************************************************/
   11003             : 
   11004             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   11005             :  *
   11006             :  * This is the same as the C function GDALExtendedDataTypeCreateString().
   11007             :  *
   11008             :  * @param nMaxStringLength maximum length of a string in bytes. 0 if
   11009             :  * unknown/unlimited
   11010             :  * @param eSubType Subtype.
   11011             :  */
   11012             : GDALExtendedDataType
   11013       44531 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
   11014             :                                    GDALExtendedDataTypeSubType eSubType)
   11015             : {
   11016       44531 :     return GDALExtendedDataType(nMaxStringLength, eSubType);
   11017             : }
   11018             : 
   11019             : /************************************************************************/
   11020             : /*                             operator==()                             */
   11021             : /************************************************************************/
   11022             : 
   11023             : /** Equality operator.
   11024             :  *
   11025             :  * This is the same as the C function GDALExtendedDataTypeEquals().
   11026             :  */
   11027        5325 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
   11028             : {
   11029        5298 :     if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
   11030       10623 :         m_nSize != other.m_nSize || m_osName != other.m_osName)
   11031             :     {
   11032         767 :         return false;
   11033             :     }
   11034        4558 :     if (m_eClass == GEDTC_NUMERIC)
   11035             :     {
   11036        1406 :         return m_eNumericDT == other.m_eNumericDT;
   11037             :     }
   11038        3152 :     if (m_eClass == GEDTC_STRING)
   11039             :     {
   11040        2762 :         return true;
   11041             :     }
   11042         390 :     CPLAssert(m_eClass == GEDTC_COMPOUND);
   11043         390 :     if (m_aoComponents.size() != other.m_aoComponents.size())
   11044             :     {
   11045           2 :         return false;
   11046             :     }
   11047        2324 :     for (size_t i = 0; i < m_aoComponents.size(); i++)
   11048             :     {
   11049        1936 :         if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
   11050             :         {
   11051           0 :             return false;
   11052             :         }
   11053             :     }
   11054         388 :     return true;
   11055             : }
   11056             : 
   11057             : /************************************************************************/
   11058             : /*                            CanConvertTo()                            */
   11059             : /************************************************************************/
   11060             : 
   11061             : /** Return whether this data type can be converted to the other one.
   11062             :  *
   11063             :  * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
   11064             :  *
   11065             :  * @param other Target data type for the conversion being considered.
   11066             :  */
   11067       22922 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
   11068             : {
   11069       22922 :     if (m_eClass == GEDTC_NUMERIC)
   11070             :     {
   11071       16723 :         if (m_eNumericDT == GDT_Unknown)
   11072           0 :             return false;
   11073       16723 :         if (other.m_eClass == GEDTC_NUMERIC &&
   11074       15287 :             other.m_eNumericDT == GDT_Unknown)
   11075           0 :             return false;
   11076       18159 :         return other.m_eClass == GEDTC_NUMERIC ||
   11077       18159 :                other.m_eClass == GEDTC_STRING;
   11078             :     }
   11079        6199 :     if (m_eClass == GEDTC_STRING)
   11080             :     {
   11081        5605 :         return other.m_eClass == m_eClass;
   11082             :     }
   11083         594 :     CPLAssert(m_eClass == GEDTC_COMPOUND);
   11084         594 :     if (other.m_eClass != GEDTC_COMPOUND)
   11085           0 :         return false;
   11086             :     std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
   11087        1188 :         srcComponents;
   11088        2822 :     for (const auto &srcComp : m_aoComponents)
   11089             :     {
   11090        2228 :         srcComponents[srcComp->GetName()] = &srcComp;
   11091             :     }
   11092        1345 :     for (const auto &dstComp : other.m_aoComponents)
   11093             :     {
   11094         752 :         auto oIter = srcComponents.find(dstComp->GetName());
   11095         752 :         if (oIter == srcComponents.end())
   11096           1 :             return false;
   11097         751 :         if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
   11098           0 :             return false;
   11099             :     }
   11100         593 :     return true;
   11101             : }
   11102             : 
   11103             : /************************************************************************/
   11104             : /*                       NeedsFreeDynamicMemory()                       */
   11105             : /************************************************************************/
   11106             : 
   11107             : /** Return whether the data type holds dynamically allocated memory, that
   11108             :  * needs to be freed with FreeDynamicMemory().
   11109             :  *
   11110             :  */
   11111        6435 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
   11112             : {
   11113        6435 :     switch (m_eClass)
   11114             :     {
   11115        1751 :         case GEDTC_STRING:
   11116        1751 :             return true;
   11117             : 
   11118        4424 :         case GEDTC_NUMERIC:
   11119        4424 :             return false;
   11120             : 
   11121         260 :         case GEDTC_COMPOUND:
   11122             :         {
   11123         388 :             for (const auto &comp : m_aoComponents)
   11124             :             {
   11125         366 :                 if (comp->GetType().NeedsFreeDynamicMemory())
   11126         238 :                     return true;
   11127             :             }
   11128             :         }
   11129             :     }
   11130          22 :     return false;
   11131             : }
   11132             : 
   11133             : /************************************************************************/
   11134             : /*                         FreeDynamicMemory()                          */
   11135             : /************************************************************************/
   11136             : 
   11137             : /** Release the dynamic memory (strings typically) from a raw value.
   11138             :  *
   11139             :  * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
   11140             :  *
   11141             :  * @param pBuffer Raw buffer of a single element of an attribute or array value.
   11142             :  */
   11143        8780 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
   11144             : {
   11145        8780 :     switch (m_eClass)
   11146             :     {
   11147        5052 :         case GEDTC_STRING:
   11148             :         {
   11149             :             char *pszStr;
   11150        5052 :             memcpy(&pszStr, pBuffer, sizeof(char *));
   11151        5052 :             if (pszStr)
   11152             :             {
   11153        3813 :                 VSIFree(pszStr);
   11154             :             }
   11155        5052 :             break;
   11156             :         }
   11157             : 
   11158        3399 :         case GEDTC_NUMERIC:
   11159             :         {
   11160        3399 :             break;
   11161             :         }
   11162             : 
   11163         329 :         case GEDTC_COMPOUND:
   11164             :         {
   11165         329 :             GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
   11166        2069 :             for (const auto &comp : m_aoComponents)
   11167             :             {
   11168        3480 :                 comp->GetType().FreeDynamicMemory(pabyBuffer +
   11169        1740 :                                                   comp->GetOffset());
   11170             :             }
   11171         329 :             break;
   11172             :         }
   11173             :     }
   11174        8780 : }
   11175             : 
   11176             : /************************************************************************/
   11177             : /*                         ~GDALEDTComponent()                          */
   11178             : /************************************************************************/
   11179             : 
   11180             : GDALEDTComponent::~GDALEDTComponent() = default;
   11181             : 
   11182             : /************************************************************************/
   11183             : /*                          GDALEDTComponent()                          */
   11184             : /************************************************************************/
   11185             : 
   11186             : /** constructor of a GDALEDTComponent
   11187             :  *
   11188             :  * This is the same as the C function GDALEDTComponendCreate()
   11189             :  *
   11190             :  * @param name Component name
   11191             :  * @param offset Offset in byte of the component in the compound data type.
   11192             :  *               In case of nesting of compound data type, this should be
   11193             :  *               the offset to the immediate belonging data type, not to the
   11194             :  *               higher level one.
   11195             :  * @param type   Component data type.
   11196             :  */
   11197        5734 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
   11198        5734 :                                    const GDALExtendedDataType &type)
   11199        5734 :     : m_osName(name), m_nOffset(offset), m_oType(type)
   11200             : {
   11201        5734 : }
   11202             : 
   11203             : /************************************************************************/
   11204             : /*                          GDALEDTComponent()                          */
   11205             : /************************************************************************/
   11206             : 
   11207             : /** Copy constructor. */
   11208             : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
   11209             : 
   11210             : /************************************************************************/
   11211             : /*                             operator==()                             */
   11212             : /************************************************************************/
   11213             : 
   11214             : /** Equality operator.
   11215             :  */
   11216        1936 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
   11217             : {
   11218        3872 :     return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
   11219        3872 :            m_oType == other.m_oType;
   11220             : }
   11221             : 
   11222             : /************************************************************************/
   11223             : /*                           ~GDALDimension()                           */
   11224             : /************************************************************************/
   11225             : 
   11226             : GDALDimension::~GDALDimension() = default;
   11227             : 
   11228             : /************************************************************************/
   11229             : /*                           GDALDimension()                            */
   11230             : /************************************************************************/
   11231             : 
   11232             : //! @cond Doxygen_Suppress
   11233             : /** Constructor.
   11234             :  *
   11235             :  * @param osParentName Parent name
   11236             :  * @param osName name
   11237             :  * @param osType type. See GetType().
   11238             :  * @param osDirection direction. See GetDirection().
   11239             :  * @param nSize size.
   11240             :  */
   11241       13035 : GDALDimension::GDALDimension(const std::string &osParentName,
   11242             :                              const std::string &osName,
   11243             :                              const std::string &osType,
   11244       13035 :                              const std::string &osDirection, GUInt64 nSize)
   11245             :     : m_osName(osName),
   11246             :       m_osFullName(
   11247       13035 :           !osParentName.empty()
   11248       18195 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
   11249             :               : osName),
   11250       44265 :       m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
   11251             : {
   11252       13035 : }
   11253             : 
   11254             : //! @endcond
   11255             : 
   11256             : /************************************************************************/
   11257             : /*                        GetIndexingVariable()                         */
   11258             : /************************************************************************/
   11259             : 
   11260             : /** Return the variable that is used to index the dimension (if there is one).
   11261             :  *
   11262             :  * This is the array, typically one-dimensional, describing the values taken
   11263             :  * by the dimension.
   11264             :  */
   11265          67 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
   11266             : {
   11267          67 :     return nullptr;
   11268             : }
   11269             : 
   11270             : /************************************************************************/
   11271             : /*                        SetIndexingVariable()                         */
   11272             : /************************************************************************/
   11273             : 
   11274             : /** Set the variable that is used to index the dimension.
   11275             :  *
   11276             :  * This is the array, typically one-dimensional, describing the values taken
   11277             :  * by the dimension.
   11278             :  *
   11279             :  * Optionally implemented by drivers.
   11280             :  *
   11281             :  * Drivers known to implement it: MEM.
   11282             :  *
   11283             :  * @param poArray Variable to use to index the dimension.
   11284             :  * @return true in case of success.
   11285             :  */
   11286          11 : bool GDALDimension::SetIndexingVariable(
   11287             :     CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
   11288             : {
   11289          11 :     CPLError(CE_Failure, CPLE_NotSupported,
   11290             :              "SetIndexingVariable() not implemented");
   11291          11 :     return false;
   11292             : }
   11293             : 
   11294             : /************************************************************************/
   11295             : /*                               Rename()                               */
   11296             : /************************************************************************/
   11297             : 
   11298             : /** Rename the dimension.
   11299             :  *
   11300             :  * This is not implemented by all drivers.
   11301             :  *
   11302             :  * Drivers known to implement it: MEM, netCDF, ZARR.
   11303             :  *
   11304             :  * This is the same as the C function GDALDimensionRename().
   11305             :  *
   11306             :  * @param osNewName New name.
   11307             :  *
   11308             :  * @return true in case of success
   11309             :  * @since GDAL 3.8
   11310             :  */
   11311           0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
   11312             : {
   11313           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
   11314           0 :     return false;
   11315             : }
   11316             : 
   11317             : /************************************************************************/
   11318             : /*                             BaseRename()                             */
   11319             : /************************************************************************/
   11320             : 
   11321             : //! @cond Doxygen_Suppress
   11322           8 : void GDALDimension::BaseRename(const std::string &osNewName)
   11323             : {
   11324           8 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
   11325           8 :     m_osFullName += osNewName;
   11326           8 :     m_osName = osNewName;
   11327           8 : }
   11328             : 
   11329             : //! @endcond
   11330             : 
   11331             : //! @cond Doxygen_Suppress
   11332             : /************************************************************************/
   11333             : /*                           ParentRenamed()                            */
   11334             : /************************************************************************/
   11335             : 
   11336           8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
   11337             : {
   11338           8 :     m_osFullName = osNewParentFullName;
   11339           8 :     m_osFullName += "/";
   11340           8 :     m_osFullName += m_osName;
   11341           8 : }
   11342             : 
   11343             : //! @endcond
   11344             : 
   11345             : //! @cond Doxygen_Suppress
   11346             : /************************************************************************/
   11347             : /*                           ParentDeleted()                            */
   11348             : /************************************************************************/
   11349             : 
   11350           8 : void GDALDimension::ParentDeleted()
   11351             : {
   11352           8 : }
   11353             : 
   11354             : //! @endcond
   11355             : 
   11356             : /************************************************************************/
   11357             : /************************************************************************/
   11358             : /************************************************************************/
   11359             : /*                              C API                                   */
   11360             : /************************************************************************/
   11361             : /************************************************************************/
   11362             : /************************************************************************/
   11363             : 
   11364             : /************************************************************************/
   11365             : /*                     GDALExtendedDataTypeCreate()                     */
   11366             : /************************************************************************/
   11367             : 
   11368             : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
   11369             :  *
   11370             :  * This is the same as the C++ method GDALExtendedDataType::Create()
   11371             :  *
   11372             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   11373             :  *
   11374             :  * @param eType Numeric data type. Must be different from GDT_Unknown and
   11375             :  * GDT_TypeCount
   11376             :  *
   11377             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   11378             :  */
   11379        2454 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
   11380             : {
   11381        2454 :     if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
   11382             :     {
   11383           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
   11384             :                  "Illegal GDT_Unknown/GDT_TypeCount argument");
   11385           0 :         return nullptr;
   11386             :     }
   11387             :     return new GDALExtendedDataTypeHS(
   11388        2454 :         new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
   11389             : }
   11390             : 
   11391             : /************************************************************************/
   11392             : /*                  GDALExtendedDataTypeCreateString()                  */
   11393             : /************************************************************************/
   11394             : 
   11395             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   11396             :  *
   11397             :  * This is the same as the C++ method GDALExtendedDataType::CreateString()
   11398             :  *
   11399             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   11400             :  *
   11401             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   11402             :  */
   11403           0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
   11404             : {
   11405           0 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   11406           0 :         GDALExtendedDataType::CreateString(nMaxStringLength)));
   11407             : }
   11408             : 
   11409             : /************************************************************************/
   11410             : /*                 GDALExtendedDataTypeCreateStringEx()                 */
   11411             : /************************************************************************/
   11412             : 
   11413             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   11414             :  *
   11415             :  * This is the same as the C++ method GDALExtendedDataType::CreateString()
   11416             :  *
   11417             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   11418             :  *
   11419             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   11420             :  * @since GDAL 3.4
   11421             :  */
   11422             : GDALExtendedDataTypeH
   11423         233 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
   11424             :                                    GDALExtendedDataTypeSubType eSubType)
   11425             : {
   11426         233 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   11427         233 :         GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
   11428             : }
   11429             : 
   11430             : /************************************************************************/
   11431             : /*                 GDALExtendedDataTypeCreateCompound()                 */
   11432             : /************************************************************************/
   11433             : 
   11434             : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
   11435             :  *
   11436             :  * This is the same as the C++ method GDALExtendedDataType::Create(const
   11437             :  * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
   11438             :  *
   11439             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   11440             :  *
   11441             :  * @param pszName Type name.
   11442             :  * @param nTotalSize Total size of the type in bytes.
   11443             :  *                   Should be large enough to store all components.
   11444             :  * @param nComponents Number of components in comps array.
   11445             :  * @param comps Components.
   11446             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   11447             :  */
   11448             : GDALExtendedDataTypeH
   11449          22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
   11450             :                                    size_t nComponents,
   11451             :                                    const GDALEDTComponentH *comps)
   11452             : {
   11453          44 :     std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
   11454          54 :     for (size_t i = 0; i < nComponents; i++)
   11455             :     {
   11456             :         compsCpp.emplace_back(
   11457          32 :             std::make_unique<GDALEDTComponent>(*(comps[i]->m_poImpl.get())));
   11458             :     }
   11459             :     auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
   11460          66 :                                            std::move(compsCpp));
   11461          22 :     if (dt.GetClass() != GEDTC_COMPOUND)
   11462           6 :         return nullptr;
   11463          16 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(std::move(dt)));
   11464             : }
   11465             : 
   11466             : /************************************************************************/
   11467             : /*                    GDALExtendedDataTypeRelease()                     */
   11468             : /************************************************************************/
   11469             : 
   11470             : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
   11471             :  *
   11472             :  * Note: when applied on a object coming from a driver, this does not
   11473             :  * destroy the object in the file, database, etc...
   11474             :  */
   11475        9479 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
   11476             : {
   11477        9479 :     delete hEDT;
   11478        9479 : }
   11479             : 
   11480             : /************************************************************************/
   11481             : /*                    GDALExtendedDataTypeGetName()                     */
   11482             : /************************************************************************/
   11483             : 
   11484             : /** Return type name.
   11485             :  *
   11486             :  * This is the same as the C++ method GDALExtendedDataType::GetName()
   11487             :  */
   11488           8 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
   11489             : {
   11490           8 :     VALIDATE_POINTER1(hEDT, __func__, "");
   11491           8 :     return hEDT->m_poImpl->GetName().c_str();
   11492             : }
   11493             : 
   11494             : /************************************************************************/
   11495             : /*                    GDALExtendedDataTypeGetClass()                    */
   11496             : /************************************************************************/
   11497             : 
   11498             : /** Return type class.
   11499             :  *
   11500             :  * This is the same as the C++ method GDALExtendedDataType::GetClass()
   11501             :  */
   11502             : GDALExtendedDataTypeClass
   11503       14625 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
   11504             : {
   11505       14625 :     VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
   11506       14625 :     return hEDT->m_poImpl->GetClass();
   11507             : }
   11508             : 
   11509             : /************************************************************************/
   11510             : /*               GDALExtendedDataTypeGetNumericDataType()               */
   11511             : /************************************************************************/
   11512             : 
   11513             : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
   11514             :  *
   11515             :  * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
   11516             :  */
   11517        3574 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
   11518             : {
   11519        3574 :     VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
   11520        3574 :     return hEDT->m_poImpl->GetNumericDataType();
   11521             : }
   11522             : 
   11523             : /************************************************************************/
   11524             : /*                    GDALExtendedDataTypeGetSize()                     */
   11525             : /************************************************************************/
   11526             : 
   11527             : /** Return data type size in bytes.
   11528             :  *
   11529             :  * This is the same as the C++ method GDALExtendedDataType::GetSize()
   11530             :  */
   11531        3926 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
   11532             : {
   11533        3926 :     VALIDATE_POINTER1(hEDT, __func__, 0);
   11534        3926 :     return hEDT->m_poImpl->GetSize();
   11535             : }
   11536             : 
   11537             : /************************************************************************/
   11538             : /*               GDALExtendedDataTypeGetMaxStringLength()               */
   11539             : /************************************************************************/
   11540             : 
   11541             : /** Return the maximum length of a string in bytes.
   11542             :  *
   11543             :  * 0 indicates unknown/unlimited string.
   11544             :  *
   11545             :  * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
   11546             :  */
   11547           7 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
   11548             : {
   11549           7 :     VALIDATE_POINTER1(hEDT, __func__, 0);
   11550           7 :     return hEDT->m_poImpl->GetMaxStringLength();
   11551             : }
   11552             : 
   11553             : /************************************************************************/
   11554             : /*                  GDALExtendedDataTypeCanConvertTo()                  */
   11555             : /************************************************************************/
   11556             : 
   11557             : /** Return whether this data type can be converted to the other one.
   11558             :  *
   11559             :  * This is the same as the C function GDALExtendedDataType::CanConvertTo()
   11560             :  *
   11561             :  * @param hSourceEDT Source data type for the conversion being considered.
   11562             :  * @param hTargetEDT Target data type for the conversion being considered.
   11563             :  * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
   11564             :  */
   11565           7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
   11566             :                                      GDALExtendedDataTypeH hTargetEDT)
   11567             : {
   11568           7 :     VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
   11569           7 :     VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
   11570           7 :     return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
   11571             : }
   11572             : 
   11573             : /************************************************************************/
   11574             : /*                     GDALExtendedDataTypeEquals()                     */
   11575             : /************************************************************************/
   11576             : 
   11577             : /** Return whether this data type is equal to another one.
   11578             :  *
   11579             :  * This is the same as the C++ method GDALExtendedDataType::operator==()
   11580             :  *
   11581             :  * @param hFirstEDT First data type.
   11582             :  * @param hSecondEDT Second data type.
   11583             :  * @return TRUE if they are equal. FALSE otherwise.
   11584             :  */
   11585         102 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
   11586             :                                GDALExtendedDataTypeH hSecondEDT)
   11587             : {
   11588         102 :     VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
   11589         102 :     VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
   11590         102 :     return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
   11591             : }
   11592             : 
   11593             : /************************************************************************/
   11594             : /*                   GDALExtendedDataTypeGetSubType()                   */
   11595             : /************************************************************************/
   11596             : 
   11597             : /** Return the subtype of a type.
   11598             :  *
   11599             :  * This is the same as the C++ method GDALExtendedDataType::GetSubType()
   11600             :  *
   11601             :  * @param hEDT Data type.
   11602             :  * @return subtype.
   11603             :  * @since 3.4
   11604             :  */
   11605             : GDALExtendedDataTypeSubType
   11606         116 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
   11607             : {
   11608         116 :     VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
   11609         116 :     return hEDT->m_poImpl->GetSubType();
   11610             : }
   11611             : 
   11612             : /************************************************************************/
   11613             : /*                     GDALExtendedDataTypeGetRAT()                     */
   11614             : /************************************************************************/
   11615             : 
   11616             : /** Return associated raster attribute table, when there is one.
   11617             :  *
   11618             :  * * For the netCDF driver, the RAT will capture enumerated types, with
   11619             :  * a "value" column with an integer value and a "name" column with the
   11620             :  * associated name.
   11621             :  * This is the same as the C++ method GDALExtendedDataType::GetRAT()
   11622             :  *
   11623             :  * @param hEDT Data type.
   11624             :  * @return raster attribute (owned by GDALExtendedDataTypeH), or NULL
   11625             :  * @since 3.12
   11626             :  */
   11627           1 : GDALRasterAttributeTableH GDALExtendedDataTypeGetRAT(GDALExtendedDataTypeH hEDT)
   11628             : {
   11629           1 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11630           1 :     return GDALRasterAttributeTable::ToHandle(
   11631           2 :         const_cast<GDALRasterAttributeTable *>(hEDT->m_poImpl->GetRAT()));
   11632             : }
   11633             : 
   11634             : /************************************************************************/
   11635             : /*                 GDALExtendedDataTypeGetComponents()                  */
   11636             : /************************************************************************/
   11637             : 
   11638             : /** Return the components of the data type (only valid when GetClass() ==
   11639             :  * GEDTC_COMPOUND)
   11640             :  *
   11641             :  * The returned array and its content must be freed with
   11642             :  * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
   11643             :  * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
   11644             :  * individual array members).
   11645             :  *
   11646             :  * This is the same as the C++ method GDALExtendedDataType::GetComponents()
   11647             :  *
   11648             :  * @param hEDT Data type
   11649             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11650             :  * @return an array of *pnCount components.
   11651             :  */
   11652          44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
   11653             :                                                      size_t *pnCount)
   11654             : {
   11655          44 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11656          44 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11657          44 :     const auto &components = hEDT->m_poImpl->GetComponents();
   11658             :     auto ret = static_cast<GDALEDTComponentH *>(
   11659          44 :         CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
   11660         131 :     for (size_t i = 0; i < components.size(); i++)
   11661             :     {
   11662          87 :         ret[i] = new GDALEDTComponentHS(*components[i].get());
   11663             :     }
   11664          44 :     *pnCount = components.size();
   11665          44 :     return ret;
   11666             : }
   11667             : 
   11668             : /************************************************************************/
   11669             : /*                 GDALExtendedDataTypeFreeComponents()                 */
   11670             : /************************************************************************/
   11671             : 
   11672             : /** Free the return of GDALExtendedDataTypeGetComponents().
   11673             :  *
   11674             :  * @param components return value of GDALExtendedDataTypeGetComponents()
   11675             :  * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
   11676             :  */
   11677          44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
   11678             :                                         size_t nCount)
   11679             : {
   11680         131 :     for (size_t i = 0; i < nCount; i++)
   11681             :     {
   11682          87 :         delete components[i];
   11683             :     }
   11684          44 :     CPLFree(components);
   11685          44 : }
   11686             : 
   11687             : /************************************************************************/
   11688             : /*                       GDALEDTComponentCreate()                       */
   11689             : /************************************************************************/
   11690             : 
   11691             : /** Create a new GDALEDTComponent.
   11692             :  *
   11693             :  * The returned value must be freed with GDALEDTComponentRelease().
   11694             :  *
   11695             :  * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
   11696             :  */
   11697          20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
   11698             :                                          GDALExtendedDataTypeH hType)
   11699             : {
   11700          20 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11701          20 :     VALIDATE_POINTER1(hType, __func__, nullptr);
   11702             :     return new GDALEDTComponentHS(
   11703          20 :         GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
   11704             : }
   11705             : 
   11706             : /************************************************************************/
   11707             : /*                      GDALEDTComponentRelease()                       */
   11708             : /************************************************************************/
   11709             : 
   11710             : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
   11711             :  *
   11712             :  * Note: when applied on a object coming from a driver, this does not
   11713             :  * destroy the object in the file, database, etc...
   11714             :  */
   11715          61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
   11716             : {
   11717          61 :     delete hComp;
   11718          61 : }
   11719             : 
   11720             : /************************************************************************/
   11721             : /*                      GDALEDTComponentGetName()                       */
   11722             : /************************************************************************/
   11723             : 
   11724             : /** Return the name.
   11725             :  *
   11726             :  * The returned pointer is valid until hComp is released.
   11727             :  *
   11728             :  * This is the same as the C++ method GDALEDTComponent::GetName().
   11729             :  */
   11730          33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
   11731             : {
   11732          33 :     VALIDATE_POINTER1(hComp, __func__, nullptr);
   11733          33 :     return hComp->m_poImpl->GetName().c_str();
   11734             : }
   11735             : 
   11736             : /************************************************************************/
   11737             : /*                     GDALEDTComponentGetOffset()                      */
   11738             : /************************************************************************/
   11739             : 
   11740             : /** Return the offset (in bytes) of the component in the compound data type.
   11741             :  *
   11742             :  * This is the same as the C++ method GDALEDTComponent::GetOffset().
   11743             :  */
   11744          31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
   11745             : {
   11746          31 :     VALIDATE_POINTER1(hComp, __func__, 0);
   11747          31 :     return hComp->m_poImpl->GetOffset();
   11748             : }
   11749             : 
   11750             : /************************************************************************/
   11751             : /*                      GDALEDTComponentGetType()                       */
   11752             : /************************************************************************/
   11753             : 
   11754             : /** Return the data type of the component.
   11755             :  *
   11756             :  * This is the same as the C++ method GDALEDTComponent::GetType().
   11757             :  */
   11758          93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
   11759             : {
   11760          93 :     VALIDATE_POINTER1(hComp, __func__, nullptr);
   11761             :     return new GDALExtendedDataTypeHS(
   11762          93 :         new GDALExtendedDataType(hComp->m_poImpl->GetType()));
   11763             : }
   11764             : 
   11765             : /************************************************************************/
   11766             : /*                          GDALGroupRelease()                          */
   11767             : /************************************************************************/
   11768             : 
   11769             : /** Release the GDAL in-memory object associated with a GDALGroupH.
   11770             :  *
   11771             :  * Note: when applied on a object coming from a driver, this does not
   11772             :  * destroy the object in the file, database, etc...
   11773             :  */
   11774        2401 : void GDALGroupRelease(GDALGroupH hGroup)
   11775             : {
   11776        2401 :     delete hGroup;
   11777        2401 : }
   11778             : 
   11779             : /************************************************************************/
   11780             : /*                          GDALGroupGetName()                          */
   11781             : /************************************************************************/
   11782             : 
   11783             : /** Return the name of the group.
   11784             :  *
   11785             :  * The returned pointer is valid until hGroup is released.
   11786             :  *
   11787             :  * This is the same as the C++ method GDALGroup::GetName().
   11788             :  */
   11789          97 : const char *GDALGroupGetName(GDALGroupH hGroup)
   11790             : {
   11791          97 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11792          97 :     return hGroup->m_poImpl->GetName().c_str();
   11793             : }
   11794             : 
   11795             : /************************************************************************/
   11796             : /*                        GDALGroupGetFullName()                        */
   11797             : /************************************************************************/
   11798             : 
   11799             : /** Return the full name of the group.
   11800             :  *
   11801             :  * The returned pointer is valid until hGroup is released.
   11802             :  *
   11803             :  * This is the same as the C++ method GDALGroup::GetFullName().
   11804             :  */
   11805          49 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
   11806             : {
   11807          49 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11808          49 :     return hGroup->m_poImpl->GetFullName().c_str();
   11809             : }
   11810             : 
   11811             : /************************************************************************/
   11812             : /*                      GDALGroupGetMDArrayNames()                      */
   11813             : /************************************************************************/
   11814             : 
   11815             : /** Return the list of multidimensional array names contained in this group.
   11816             :  *
   11817             :  * This is the same as the C++ method GDALGroup::GetGroupNames().
   11818             :  *
   11819             :  * @return the array names, to be freed with CSLDestroy()
   11820             :  */
   11821         408 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
   11822             : {
   11823         408 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11824         816 :     auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
   11825         816 :     CPLStringList res;
   11826         993 :     for (const auto &name : names)
   11827             :     {
   11828         585 :         res.AddString(name.c_str());
   11829             :     }
   11830         408 :     return res.StealList();
   11831             : }
   11832             : 
   11833             : /************************************************************************/
   11834             : /*               GDALGroupGetMDArrayFullNamesRecursive()                */
   11835             : /************************************************************************/
   11836             : 
   11837             : /** Return the list of multidimensional array full names contained in this
   11838             :  * group and its subgroups.
   11839             :  *
   11840             :  * This is the same as the C++ method GDALGroup::GetMDArrayFullNamesRecursive().
   11841             :  *
   11842             :  * @return the array names, to be freed with CSLDestroy()
   11843             :  *
   11844             :  * @since 3.11
   11845             :  */
   11846           1 : char **GDALGroupGetMDArrayFullNamesRecursive(GDALGroupH hGroup,
   11847             :                                              CSLConstList papszGroupOptions,
   11848             :                                              CSLConstList papszArrayOptions)
   11849             : {
   11850           1 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11851           1 :     auto names = hGroup->m_poImpl->GetMDArrayFullNamesRecursive(
   11852           2 :         papszGroupOptions, papszArrayOptions);
   11853           2 :     CPLStringList res;
   11854           5 :     for (const auto &name : names)
   11855             :     {
   11856           4 :         res.AddString(name.c_str());
   11857             :     }
   11858           1 :     return res.StealList();
   11859             : }
   11860             : 
   11861             : /************************************************************************/
   11862             : /*                        GDALGroupOpenMDArray()                        */
   11863             : /************************************************************************/
   11864             : 
   11865             : /** Open and return a multidimensional array.
   11866             :  *
   11867             :  * This is the same as the C++ method GDALGroup::OpenMDArray().
   11868             :  *
   11869             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11870             :  */
   11871        1637 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
   11872             :                                   CSLConstList papszOptions)
   11873             : {
   11874        1637 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11875        1637 :     VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
   11876        4911 :     auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
   11877        4911 :                                                papszOptions);
   11878        1637 :     if (!array)
   11879          30 :         return nullptr;
   11880        1607 :     return new GDALMDArrayHS(array);
   11881             : }
   11882             : 
   11883             : /************************************************************************/
   11884             : /*                  GDALGroupOpenMDArrayFromFullname()                  */
   11885             : /************************************************************************/
   11886             : 
   11887             : /** Open and return a multidimensional array from its fully qualified name.
   11888             :  *
   11889             :  * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
   11890             :  *
   11891             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11892             :  *
   11893             :  * @since GDAL 3.2
   11894             :  */
   11895          18 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
   11896             :                                               const char *pszFullname,
   11897             :                                               CSLConstList papszOptions)
   11898             : {
   11899          18 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11900          18 :     VALIDATE_POINTER1(pszFullname, __func__, nullptr);
   11901          18 :     auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
   11902          54 :         std::string(pszFullname), papszOptions);
   11903          18 :     if (!array)
   11904           2 :         return nullptr;
   11905          16 :     return new GDALMDArrayHS(array);
   11906             : }
   11907             : 
   11908             : /************************************************************************/
   11909             : /*                      GDALGroupResolveMDArray()                       */
   11910             : /************************************************************************/
   11911             : 
   11912             : /** Locate an array in a group and its subgroups by name.
   11913             :  *
   11914             :  * See GDALGroup::ResolveMDArray() for description of the behavior.
   11915             :  * @since GDAL 3.2
   11916             :  */
   11917          19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
   11918             :                                      const char *pszStartingPoint,
   11919             :                                      CSLConstList papszOptions)
   11920             : {
   11921          19 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11922          19 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11923          19 :     VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
   11924          19 :     auto array = hGroup->m_poImpl->ResolveMDArray(
   11925          57 :         std::string(pszName), std::string(pszStartingPoint), papszOptions);
   11926          19 :     if (!array)
   11927           2 :         return nullptr;
   11928          17 :     return new GDALMDArrayHS(array);
   11929             : }
   11930             : 
   11931             : /************************************************************************/
   11932             : /*                       GDALGroupGetGroupNames()                       */
   11933             : /************************************************************************/
   11934             : 
   11935             : /** Return the list of sub-groups contained in this group.
   11936             :  *
   11937             :  * This is the same as the C++ method GDALGroup::GetGroupNames().
   11938             :  *
   11939             :  * @return the group names, to be freed with CSLDestroy()
   11940             :  */
   11941         106 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
   11942             : {
   11943         106 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11944         212 :     auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
   11945         212 :     CPLStringList res;
   11946         241 :     for (const auto &name : names)
   11947             :     {
   11948         135 :         res.AddString(name.c_str());
   11949             :     }
   11950         106 :     return res.StealList();
   11951             : }
   11952             : 
   11953             : /************************************************************************/
   11954             : /*                         GDALGroupOpenGroup()                         */
   11955             : /************************************************************************/
   11956             : 
   11957             : /** Open and return a sub-group.
   11958             :  *
   11959             :  * This is the same as the C++ method GDALGroup::OpenGroup().
   11960             :  *
   11961             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11962             :  */
   11963         201 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11964             :                               CSLConstList papszOptions)
   11965             : {
   11966         201 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11967         201 :     VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
   11968             :     auto subGroup =
   11969         603 :         hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
   11970         201 :     if (!subGroup)
   11971          30 :         return nullptr;
   11972         171 :     return new GDALGroupHS(subGroup);
   11973             : }
   11974             : 
   11975             : /************************************************************************/
   11976             : /*                    GDALGroupGetVectorLayerNames()                    */
   11977             : /************************************************************************/
   11978             : 
   11979             : /** Return the list of layer names contained in this group.
   11980             :  *
   11981             :  * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
   11982             :  *
   11983             :  * @return the group names, to be freed with CSLDestroy()
   11984             :  * @since 3.4
   11985             :  */
   11986           8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
   11987             :                                     CSLConstList papszOptions)
   11988             : {
   11989           8 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11990          16 :     auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
   11991          16 :     CPLStringList res;
   11992          18 :     for (const auto &name : names)
   11993             :     {
   11994          10 :         res.AddString(name.c_str());
   11995             :     }
   11996           8 :     return res.StealList();
   11997             : }
   11998             : 
   11999             : /************************************************************************/
   12000             : /*                      GDALGroupOpenVectorLayer()                      */
   12001             : /************************************************************************/
   12002             : 
   12003             : /** Open and return a vector layer.
   12004             :  *
   12005             :  * This is the same as the C++ method GDALGroup::OpenVectorLayer().
   12006             :  *
   12007             :  * Note that the vector layer is owned by its parent GDALDatasetH, and thus
   12008             :  * the returned handled if only valid while the parent GDALDatasetH is kept
   12009             :  * opened.
   12010             :  *
   12011             :  * @return the vector layer, or nullptr.
   12012             :  * @since 3.4
   12013             :  */
   12014          12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
   12015             :                                    const char *pszVectorLayerName,
   12016             :                                    CSLConstList papszOptions)
   12017             : {
   12018          12 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12019          12 :     VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
   12020          24 :     return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
   12021          24 :         std::string(pszVectorLayerName), papszOptions));
   12022             : }
   12023             : 
   12024             : /************************************************************************/
   12025             : /*                  GDALGroupOpenMDArrayFromFullname()                  */
   12026             : /************************************************************************/
   12027             : 
   12028             : /** Open and return a sub-group from its fully qualified name.
   12029             :  *
   12030             :  * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
   12031             :  *
   12032             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   12033             :  *
   12034             :  * @since GDAL 3.2
   12035             :  */
   12036           4 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
   12037             :                                           const char *pszFullname,
   12038             :                                           CSLConstList papszOptions)
   12039             : {
   12040           4 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12041           4 :     VALIDATE_POINTER1(pszFullname, __func__, nullptr);
   12042           4 :     auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
   12043          12 :         std::string(pszFullname), papszOptions);
   12044           4 :     if (!subGroup)
   12045           2 :         return nullptr;
   12046           2 :     return new GDALGroupHS(subGroup);
   12047             : }
   12048             : 
   12049             : /************************************************************************/
   12050             : /*                       GDALGroupGetDimensions()                       */
   12051             : /************************************************************************/
   12052             : 
   12053             : /** Return the list of dimensions contained in this group and used by its
   12054             :  * arrays.
   12055             :  *
   12056             :  * The returned array must be freed with GDALReleaseDimensions().  If only the
   12057             :  * array itself needs to be freed, CPLFree() should be called (and
   12058             :  * GDALDimensionRelease() on individual array members).
   12059             :  *
   12060             :  * This is the same as the C++ method GDALGroup::GetDimensions().
   12061             :  *
   12062             :  * @param hGroup Group.
   12063             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12064             :  * @param papszOptions Driver specific options determining how dimensions
   12065             :  * should be retrieved. Pass nullptr for default behavior.
   12066             :  *
   12067             :  * @return an array of *pnCount dimensions.
   12068             :  */
   12069          73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
   12070             :                                        CSLConstList papszOptions)
   12071             : {
   12072          73 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12073          73 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12074          73 :     auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
   12075             :     auto ret = static_cast<GDALDimensionH *>(
   12076          73 :         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
   12077         230 :     for (size_t i = 0; i < dims.size(); i++)
   12078             :     {
   12079         157 :         ret[i] = new GDALDimensionHS(dims[i]);
   12080             :     }
   12081          73 :     *pnCount = dims.size();
   12082          73 :     return ret;
   12083             : }
   12084             : 
   12085             : /************************************************************************/
   12086             : /*                       GDALGroupGetAttribute()                        */
   12087             : /************************************************************************/
   12088             : 
   12089             : /** Return an attribute by its name.
   12090             :  *
   12091             :  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
   12092             :  *
   12093             :  * The returned attribute must be freed with GDALAttributeRelease().
   12094             :  */
   12095         121 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
   12096             : {
   12097         121 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12098         121 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   12099         363 :     auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
   12100         121 :     if (attr)
   12101         116 :         return new GDALAttributeHS(attr);
   12102           5 :     return nullptr;
   12103             : }
   12104             : 
   12105             : /************************************************************************/
   12106             : /*                       GDALGroupGetAttributes()                       */
   12107             : /************************************************************************/
   12108             : 
   12109             : /** Return the list of attributes contained in this group.
   12110             :  *
   12111             :  * The returned array must be freed with GDALReleaseAttributes(). If only the
   12112             :  * array itself needs to be freed, CPLFree() should be called (and
   12113             :  * GDALAttributeRelease() on individual array members).
   12114             :  *
   12115             :  * This is the same as the C++ method GDALGroup::GetAttributes().
   12116             :  *
   12117             :  * @param hGroup Group.
   12118             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12119             :  * @param papszOptions Driver specific options determining how attributes
   12120             :  * should be retrieved. Pass nullptr for default behavior.
   12121             :  *
   12122             :  * @return an array of *pnCount attributes.
   12123             :  */
   12124          71 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
   12125             :                                        CSLConstList papszOptions)
   12126             : {
   12127          71 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12128          71 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12129          71 :     auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
   12130             :     auto ret = static_cast<GDALAttributeH *>(
   12131          71 :         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
   12132         229 :     for (size_t i = 0; i < attrs.size(); i++)
   12133             :     {
   12134         158 :         ret[i] = new GDALAttributeHS(attrs[i]);
   12135             :     }
   12136          71 :     *pnCount = attrs.size();
   12137          71 :     return ret;
   12138             : }
   12139             : 
   12140             : /************************************************************************/
   12141             : /*                     GDALGroupGetStructuralInfo()                     */
   12142             : /************************************************************************/
   12143             : 
   12144             : /** Return structural information on the group.
   12145             :  *
   12146             :  * This may be the compression, etc..
   12147             :  *
   12148             :  * The return value should not be freed and is valid until GDALGroup is
   12149             :  * released or this function called again.
   12150             :  *
   12151             :  * This is the same as the C++ method GDALGroup::GetStructuralInfo().
   12152             :  */
   12153           4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
   12154             : {
   12155           4 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12156           4 :     return hGroup->m_poImpl->GetStructuralInfo();
   12157             : }
   12158             : 
   12159             : /************************************************************************/
   12160             : /*                     GDALGroupGetDataTypeCount()                      */
   12161             : /************************************************************************/
   12162             : 
   12163             : /** Return the number of data types associated with the group
   12164             :  * (typically enumerations).
   12165             :  *
   12166             :  * This is the same as the C++ method GDALGroup::GetDataTypes().size().
   12167             :  *
   12168             :  * @since 3.12
   12169             :  */
   12170           4 : size_t GDALGroupGetDataTypeCount(GDALGroupH hGroup)
   12171             : {
   12172           4 :     VALIDATE_POINTER1(hGroup, __func__, 0);
   12173           4 :     return hGroup->m_poImpl->GetDataTypes().size();
   12174             : }
   12175             : 
   12176             : /************************************************************************/
   12177             : /*                        GDALGroupGetDataType()                        */
   12178             : /************************************************************************/
   12179             : 
   12180             : /** Return one of the data types associated with the group.
   12181             :  *
   12182             :  * This is the same as the C++ method GDALGroup::GetDataTypes()[].
   12183             :  *
   12184             :  * @return a type to release with GDALExtendedDataTypeRelease() once done,
   12185             :  * or nullptr in case of error.
   12186             :  * @since 3.12
   12187             :  */
   12188           1 : GDALExtendedDataTypeH GDALGroupGetDataType(GDALGroupH hGroup, size_t nIdx)
   12189             : {
   12190           1 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12191           1 :     if (nIdx >= hGroup->m_poImpl->GetDataTypes().size())
   12192           0 :         return nullptr;
   12193           1 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   12194           1 :         *(hGroup->m_poImpl->GetDataTypes()[nIdx].get())));
   12195             : }
   12196             : 
   12197             : /************************************************************************/
   12198             : /*                       GDALReleaseAttributes()                        */
   12199             : /************************************************************************/
   12200             : 
   12201             : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
   12202             :  *
   12203             :  * @param attributes return pointer of above methods
   12204             :  * @param nCount *pnCount value returned by above methods
   12205             :  */
   12206         130 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
   12207             : {
   12208         418 :     for (size_t i = 0; i < nCount; i++)
   12209             :     {
   12210         288 :         delete attributes[i];
   12211             :     }
   12212         130 :     CPLFree(attributes);
   12213         130 : }
   12214             : 
   12215             : /************************************************************************/
   12216             : /*                        GDALGroupCreateGroup()                        */
   12217             : /************************************************************************/
   12218             : 
   12219             : /** Create a sub-group within a group.
   12220             :  *
   12221             :  * This is the same as the C++ method GDALGroup::CreateGroup().
   12222             :  *
   12223             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   12224             :  */
   12225         185 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   12226             :                                 CSLConstList papszOptions)
   12227             : {
   12228         185 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12229         185 :     VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
   12230         555 :     auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
   12231         555 :                                              papszOptions);
   12232         185 :     if (!ret)
   12233          52 :         return nullptr;
   12234         133 :     return new GDALGroupHS(ret);
   12235             : }
   12236             : 
   12237             : /************************************************************************/
   12238             : /*                        GDALGroupDeleteGroup()                        */
   12239             : /************************************************************************/
   12240             : 
   12241             : /** Delete a sub-group from a group.
   12242             :  *
   12243             :  * After this call, if a previously obtained instance of the deleted object
   12244             :  * is still alive, no method other than for freeing it should be invoked.
   12245             :  *
   12246             :  * This is the same as the C++ method GDALGroup::DeleteGroup().
   12247             :  *
   12248             :  * @return true in case of success.
   12249             :  * @since GDAL 3.8
   12250             :  */
   12251          20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   12252             :                           CSLConstList papszOptions)
   12253             : {
   12254          20 :     VALIDATE_POINTER1(hGroup, __func__, false);
   12255          20 :     VALIDATE_POINTER1(pszSubGroupName, __func__, false);
   12256          40 :     return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
   12257          20 :                                          papszOptions);
   12258             : }
   12259             : 
   12260             : /************************************************************************/
   12261             : /*                      GDALGroupCreateDimension()                      */
   12262             : /************************************************************************/
   12263             : 
   12264             : /** Create a dimension within a group.
   12265             :  *
   12266             :  * This is the same as the C++ method GDALGroup::CreateDimension().
   12267             :  *
   12268             :  * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
   12269             :  */
   12270         836 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
   12271             :                                         const char *pszType,
   12272             :                                         const char *pszDirection, GUInt64 nSize,
   12273             :                                         CSLConstList papszOptions)
   12274             : {
   12275         836 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12276         836 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   12277         836 :     auto ret = hGroup->m_poImpl->CreateDimension(
   12278        1672 :         std::string(pszName), std::string(pszType ? pszType : ""),
   12279        3344 :         std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
   12280         836 :     if (!ret)
   12281           9 :         return nullptr;
   12282         827 :     return new GDALDimensionHS(ret);
   12283             : }
   12284             : 
   12285             : /************************************************************************/
   12286             : /*                       GDALGroupCreateMDArray()                       */
   12287             : /************************************************************************/
   12288             : 
   12289             : /** Create a multidimensional array within a group.
   12290             :  *
   12291             :  * This is the same as the C++ method GDALGroup::CreateMDArray().
   12292             :  *
   12293             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   12294             :  */
   12295         750 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
   12296             :                                     size_t nDimensions,
   12297             :                                     GDALDimensionH *pahDimensions,
   12298             :                                     GDALExtendedDataTypeH hEDT,
   12299             :                                     CSLConstList papszOptions)
   12300             : {
   12301         750 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12302         750 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   12303         750 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   12304        1500 :     std::vector<std::shared_ptr<GDALDimension>> dims;
   12305         750 :     dims.reserve(nDimensions);
   12306        1808 :     for (size_t i = 0; i < nDimensions; i++)
   12307        1058 :         dims.push_back(pahDimensions[i]->m_poImpl);
   12308        2250 :     auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
   12309        2250 :                                                *(hEDT->m_poImpl), papszOptions);
   12310         750 :     if (!ret)
   12311          64 :         return nullptr;
   12312         686 :     return new GDALMDArrayHS(ret);
   12313             : }
   12314             : 
   12315             : /************************************************************************/
   12316             : /*                       GDALGroupDeleteMDArray()                       */
   12317             : /************************************************************************/
   12318             : 
   12319             : /** Delete an array from a group.
   12320             :  *
   12321             :  * After this call, if a previously obtained instance of the deleted object
   12322             :  * is still alive, no method other than for freeing it should be invoked.
   12323             :  *
   12324             :  * This is the same as the C++ method GDALGroup::DeleteMDArray().
   12325             :  *
   12326             :  * @return true in case of success.
   12327             :  * @since GDAL 3.8
   12328             :  */
   12329          20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
   12330             :                             CSLConstList papszOptions)
   12331             : {
   12332          20 :     VALIDATE_POINTER1(hGroup, __func__, false);
   12333          20 :     VALIDATE_POINTER1(pszName, __func__, false);
   12334          20 :     return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
   12335             : }
   12336             : 
   12337             : /************************************************************************/
   12338             : /*                      GDALGroupCreateAttribute()                      */
   12339             : /************************************************************************/
   12340             : 
   12341             : /** Create a attribute within a group.
   12342             :  *
   12343             :  * This is the same as the C++ method GDALGroup::CreateAttribute().
   12344             :  *
   12345             :  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
   12346             :  */
   12347         142 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
   12348             :                                         size_t nDimensions,
   12349             :                                         const GUInt64 *panDimensions,
   12350             :                                         GDALExtendedDataTypeH hEDT,
   12351             :                                         CSLConstList papszOptions)
   12352             : {
   12353         142 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12354         142 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   12355         284 :     std::vector<GUInt64> dims;
   12356         142 :     dims.reserve(nDimensions);
   12357         200 :     for (size_t i = 0; i < nDimensions; i++)
   12358          58 :         dims.push_back(panDimensions[i]);
   12359         142 :     auto ret = hGroup->m_poImpl->CreateAttribute(
   12360         426 :         std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
   12361         142 :     if (!ret)
   12362          18 :         return nullptr;
   12363         124 :     return new GDALAttributeHS(ret);
   12364             : }
   12365             : 
   12366             : /************************************************************************/
   12367             : /*                      GDALGroupDeleteAttribute()                      */
   12368             : /************************************************************************/
   12369             : 
   12370             : /** Delete an attribute from a group.
   12371             :  *
   12372             :  * After this call, if a previously obtained instance of the deleted object
   12373             :  * is still alive, no method other than for freeing it should be invoked.
   12374             :  *
   12375             :  * This is the same as the C++ method GDALGroup::DeleteAttribute().
   12376             :  *
   12377             :  * @return true in case of success.
   12378             :  * @since GDAL 3.8
   12379             :  */
   12380          25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
   12381             :                               CSLConstList papszOptions)
   12382             : {
   12383          25 :     VALIDATE_POINTER1(hGroup, __func__, false);
   12384          25 :     VALIDATE_POINTER1(pszName, __func__, false);
   12385          50 :     return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
   12386          25 :                                              papszOptions);
   12387             : }
   12388             : 
   12389             : /************************************************************************/
   12390             : /*                          GDALGroupRename()                           */
   12391             : /************************************************************************/
   12392             : 
   12393             : /** Rename the group.
   12394             :  *
   12395             :  * This is not implemented by all drivers.
   12396             :  *
   12397             :  * Drivers known to implement it: MEM, netCDF.
   12398             :  *
   12399             :  * This is the same as the C++ method GDALGroup::Rename()
   12400             :  *
   12401             :  * @return true in case of success
   12402             :  * @since GDAL 3.8
   12403             :  */
   12404          45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
   12405             : {
   12406          45 :     VALIDATE_POINTER1(hGroup, __func__, false);
   12407          45 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   12408          45 :     return hGroup->m_poImpl->Rename(pszNewName);
   12409             : }
   12410             : 
   12411             : /************************************************************************/
   12412             : /*               GDALGroupSubsetDimensionFromSelection()                */
   12413             : /************************************************************************/
   12414             : 
   12415             : /** Return a virtual group whose one dimension has been subset according to a
   12416             :  * selection.
   12417             :  *
   12418             :  * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
   12419             :  *
   12420             :  * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
   12421             :  */
   12422             : GDALGroupH
   12423          14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
   12424             :                                       const char *pszSelection,
   12425             :                                       CPL_UNUSED CSLConstList papszOptions)
   12426             : {
   12427          14 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12428          14 :     VALIDATE_POINTER1(pszSelection, __func__, nullptr);
   12429          14 :     auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
   12430          42 :         std::string(pszSelection));
   12431          14 :     if (!hNewGroup)
   12432           8 :         return nullptr;
   12433           6 :     return new GDALGroupHS(hNewGroup);
   12434             : }
   12435             : 
   12436             : /************************************************************************/
   12437             : /*                         GDALMDArrayRelease()                         */
   12438             : /************************************************************************/
   12439             : 
   12440             : /** Release the GDAL in-memory object associated with a GDALMDArray.
   12441             :  *
   12442             :  * Note: when applied on a object coming from a driver, this does not
   12443             :  * destroy the object in the file, database, etc...
   12444             :  */
   12445        3071 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
   12446             : {
   12447        3071 :     delete hMDArray;
   12448        3071 : }
   12449             : 
   12450             : /************************************************************************/
   12451             : /*                         GDALMDArrayGetName()                         */
   12452             : /************************************************************************/
   12453             : 
   12454             : /** Return array name.
   12455             :  *
   12456             :  * This is the same as the C++ method GDALMDArray::GetName()
   12457             :  */
   12458          84 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
   12459             : {
   12460          84 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12461          84 :     return hArray->m_poImpl->GetName().c_str();
   12462             : }
   12463             : 
   12464             : /************************************************************************/
   12465             : /*                       GDALMDArrayGetFullName()                       */
   12466             : /************************************************************************/
   12467             : 
   12468             : /** Return array full name.
   12469             :  *
   12470             :  * This is the same as the C++ method GDALMDArray::GetFullName()
   12471             :  */
   12472          71 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
   12473             : {
   12474          71 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12475          71 :     return hArray->m_poImpl->GetFullName().c_str();
   12476             : }
   12477             : 
   12478             : /************************************************************************/
   12479             : /*                         GDALMDArrayGetName()                         */
   12480             : /************************************************************************/
   12481             : 
   12482             : /** Return the total number of values in the array.
   12483             :  *
   12484             :  * This is the same as the C++ method
   12485             :  * GDALAbstractMDArray::GetTotalElementsCount()
   12486             :  */
   12487           6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
   12488             : {
   12489           6 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12490           6 :     return hArray->m_poImpl->GetTotalElementsCount();
   12491             : }
   12492             : 
   12493             : /************************************************************************/
   12494             : /*                    GDALMDArrayGetDimensionCount()                    */
   12495             : /************************************************************************/
   12496             : 
   12497             : /** Return the number of dimensions.
   12498             :  *
   12499             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
   12500             :  */
   12501       16438 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
   12502             : {
   12503       16438 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12504       16438 :     return hArray->m_poImpl->GetDimensionCount();
   12505             : }
   12506             : 
   12507             : /************************************************************************/
   12508             : /*                      GDALMDArrayGetDimensions()                      */
   12509             : /************************************************************************/
   12510             : 
   12511             : /** Return the dimensions of the array
   12512             :  *
   12513             :  * The returned array must be freed with GDALReleaseDimensions(). If only the
   12514             :  * array itself needs to be freed, CPLFree() should be called (and
   12515             :  * GDALDimensionRelease() on individual array members).
   12516             :  *
   12517             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
   12518             :  *
   12519             :  * @param hArray Array.
   12520             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12521             :  *
   12522             :  * @return an array of *pnCount dimensions.
   12523             :  */
   12524        3505 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
   12525             : {
   12526        3505 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12527        3505 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12528        3505 :     const auto &dims(hArray->m_poImpl->GetDimensions());
   12529             :     auto ret = static_cast<GDALDimensionH *>(
   12530        3505 :         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
   12531       10064 :     for (size_t i = 0; i < dims.size(); i++)
   12532             :     {
   12533        6559 :         ret[i] = new GDALDimensionHS(dims[i]);
   12534             :     }
   12535        3505 :     *pnCount = dims.size();
   12536        3505 :     return ret;
   12537             : }
   12538             : 
   12539             : /************************************************************************/
   12540             : /*                       GDALReleaseDimensions()                        */
   12541             : /************************************************************************/
   12542             : 
   12543             : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
   12544             :  *
   12545             :  * @param dims return pointer of above methods
   12546             :  * @param nCount *pnCount value returned by above methods
   12547             :  */
   12548        3578 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
   12549             : {
   12550       10294 :     for (size_t i = 0; i < nCount; i++)
   12551             :     {
   12552        6716 :         delete dims[i];
   12553             :     }
   12554        3578 :     CPLFree(dims);
   12555        3578 : }
   12556             : 
   12557             : /************************************************************************/
   12558             : /*                       GDALMDArrayGetDataType()                       */
   12559             : /************************************************************************/
   12560             : 
   12561             : /** Return the data type
   12562             :  *
   12563             :  * The return must be freed with GDALExtendedDataTypeRelease().
   12564             :  */
   12565        6224 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
   12566             : {
   12567        6224 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12568             :     return new GDALExtendedDataTypeHS(
   12569        6224 :         new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
   12570             : }
   12571             : 
   12572             : /************************************************************************/
   12573             : /*                          GDALMDArrayRead()                           */
   12574             : /************************************************************************/
   12575             : 
   12576             : /** Read part or totality of a multidimensional array.
   12577             :  *
   12578             :  * This is the same as the C++ method GDALAbstractMDArray::Read()
   12579             :  *
   12580             :  * @return TRUE in case of success.
   12581             :  */
   12582        3029 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   12583             :                     const size_t *count, const GInt64 *arrayStep,
   12584             :                     const GPtrDiff_t *bufferStride,
   12585             :                     GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
   12586             :                     const void *pDstBufferAllocStart,
   12587             :                     size_t nDstBufferAllocSize)
   12588             : {
   12589        3029 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12590        3029 :     if ((arrayStartIdx == nullptr || count == nullptr) &&
   12591           0 :         hArray->m_poImpl->GetDimensionCount() > 0)
   12592             :     {
   12593           0 :         VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
   12594           0 :         VALIDATE_POINTER1(count, __func__, FALSE);
   12595             :     }
   12596        3029 :     VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
   12597        3029 :     VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
   12598        6058 :     return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
   12599        3029 :                                   *(bufferDataType->m_poImpl), pDstBuffer,
   12600        3029 :                                   pDstBufferAllocStart, nDstBufferAllocSize);
   12601             : }
   12602             : 
   12603             : /************************************************************************/
   12604             : /*                          GDALMDArrayWrite()                          */
   12605             : /************************************************************************/
   12606             : 
   12607             : /** Write part or totality of a multidimensional array.
   12608             :  *
   12609             :  * This is the same as the C++ method GDALAbstractMDArray::Write()
   12610             :  *
   12611             :  * @return TRUE in case of success.
   12612             :  */
   12613         892 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   12614             :                      const size_t *count, const GInt64 *arrayStep,
   12615             :                      const GPtrDiff_t *bufferStride,
   12616             :                      GDALExtendedDataTypeH bufferDataType,
   12617             :                      const void *pSrcBuffer, const void *pSrcBufferAllocStart,
   12618             :                      size_t nSrcBufferAllocSize)
   12619             : {
   12620         892 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12621         892 :     if ((arrayStartIdx == nullptr || count == nullptr) &&
   12622           0 :         hArray->m_poImpl->GetDimensionCount() > 0)
   12623             :     {
   12624           0 :         VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
   12625           0 :         VALIDATE_POINTER1(count, __func__, FALSE);
   12626             :     }
   12627         892 :     VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
   12628         892 :     VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
   12629        1784 :     return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
   12630         892 :                                    bufferStride, *(bufferDataType->m_poImpl),
   12631             :                                    pSrcBuffer, pSrcBufferAllocStart,
   12632         892 :                                    nSrcBufferAllocSize);
   12633             : }
   12634             : 
   12635             : /************************************************************************/
   12636             : /*                       GDALMDArrayAdviseRead()                        */
   12637             : /************************************************************************/
   12638             : 
   12639             : /** Advise driver of upcoming read requests.
   12640             :  *
   12641             :  * This is the same as the C++ method GDALMDArray::AdviseRead()
   12642             :  *
   12643             :  * @return TRUE in case of success.
   12644             :  *
   12645             :  * @since GDAL 3.2
   12646             :  */
   12647           0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   12648             :                           const size_t *count)
   12649             : {
   12650           0 :     return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
   12651             : }
   12652             : 
   12653             : /************************************************************************/
   12654             : /*                      GDALMDArrayAdviseReadEx()                       */
   12655             : /************************************************************************/
   12656             : 
   12657             : /** Advise driver of upcoming read requests.
   12658             :  *
   12659             :  * This is the same as the C++ method GDALMDArray::AdviseRead()
   12660             :  *
   12661             :  * @return TRUE in case of success.
   12662             :  *
   12663             :  * @since GDAL 3.4
   12664             :  */
   12665          19 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   12666             :                             const size_t *count, CSLConstList papszOptions)
   12667             : {
   12668          19 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12669          19 :     return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
   12670             : }
   12671             : 
   12672             : /************************************************************************/
   12673             : /*                      GDALMDArrayGetAttribute()                       */
   12674             : /************************************************************************/
   12675             : 
   12676             : /** Return an attribute by its name.
   12677             :  *
   12678             :  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
   12679             :  *
   12680             :  * The returned attribute must be freed with GDALAttributeRelease().
   12681             :  */
   12682         120 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
   12683             : {
   12684         120 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12685         120 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   12686         360 :     auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
   12687         120 :     if (attr)
   12688         111 :         return new GDALAttributeHS(attr);
   12689           9 :     return nullptr;
   12690             : }
   12691             : 
   12692             : /************************************************************************/
   12693             : /*                      GDALMDArrayGetAttributes()                      */
   12694             : /************************************************************************/
   12695             : 
   12696             : /** Return the list of attributes contained in this array.
   12697             :  *
   12698             :  * The returned array must be freed with GDALReleaseAttributes(). If only the
   12699             :  * array itself needs to be freed, CPLFree() should be called (and
   12700             :  * GDALAttributeRelease() on individual array members).
   12701             :  *
   12702             :  * This is the same as the C++ method GDALMDArray::GetAttributes().
   12703             :  *
   12704             :  * @param hArray Array.
   12705             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12706             :  * @param papszOptions Driver specific options determining how attributes
   12707             :  * should be retrieved. Pass nullptr for default behavior.
   12708             :  *
   12709             :  * @return an array of *pnCount attributes.
   12710             :  */
   12711          59 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
   12712             :                                          CSLConstList papszOptions)
   12713             : {
   12714          59 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12715          59 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12716          59 :     auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
   12717             :     auto ret = static_cast<GDALAttributeH *>(
   12718          59 :         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
   12719         189 :     for (size_t i = 0; i < attrs.size(); i++)
   12720             :     {
   12721         130 :         ret[i] = new GDALAttributeHS(attrs[i]);
   12722             :     }
   12723          59 :     *pnCount = attrs.size();
   12724          59 :     return ret;
   12725             : }
   12726             : 
   12727             : /************************************************************************/
   12728             : /*                     GDALMDArrayCreateAttribute()                     */
   12729             : /************************************************************************/
   12730             : 
   12731             : /** Create a attribute within an array.
   12732             :  *
   12733             :  * This is the same as the C++ method GDALMDArray::CreateAttribute().
   12734             :  *
   12735             :  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
   12736             :  */
   12737         188 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
   12738             :                                           const char *pszName,
   12739             :                                           size_t nDimensions,
   12740             :                                           const GUInt64 *panDimensions,
   12741             :                                           GDALExtendedDataTypeH hEDT,
   12742             :                                           CSLConstList papszOptions)
   12743             : {
   12744         188 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12745         188 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   12746         188 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   12747         376 :     std::vector<GUInt64> dims;
   12748         188 :     dims.reserve(nDimensions);
   12749         249 :     for (size_t i = 0; i < nDimensions; i++)
   12750          61 :         dims.push_back(panDimensions[i]);
   12751         188 :     auto ret = hArray->m_poImpl->CreateAttribute(
   12752         564 :         std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
   12753         188 :     if (!ret)
   12754           9 :         return nullptr;
   12755         179 :     return new GDALAttributeHS(ret);
   12756             : }
   12757             : 
   12758             : /************************************************************************/
   12759             : /*                     GDALMDArrayDeleteAttribute()                     */
   12760             : /************************************************************************/
   12761             : 
   12762             : /** Delete an attribute from an array.
   12763             :  *
   12764             :  * After this call, if a previously obtained instance of the deleted object
   12765             :  * is still alive, no method other than for freeing it should be invoked.
   12766             :  *
   12767             :  * This is the same as the C++ method GDALMDArray::DeleteAttribute().
   12768             :  *
   12769             :  * @return true in case of success.
   12770             :  * @since GDAL 3.8
   12771             :  */
   12772          24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
   12773             :                                 CSLConstList papszOptions)
   12774             : {
   12775          24 :     VALIDATE_POINTER1(hArray, __func__, false);
   12776          24 :     VALIDATE_POINTER1(pszName, __func__, false);
   12777          48 :     return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
   12778          24 :                                              papszOptions);
   12779             : }
   12780             : 
   12781             : /************************************************************************/
   12782             : /*                    GDALMDArrayGetRawNoDataValue()                    */
   12783             : /************************************************************************/
   12784             : 
   12785             : /** Return the nodata value as a "raw" value.
   12786             :  *
   12787             :  * The value returned might be nullptr in case of no nodata value. When
   12788             :  * a nodata value is registered, a non-nullptr will be returned whose size in
   12789             :  * bytes is GetDataType().GetSize().
   12790             :  *
   12791             :  * The returned value should not be modified or freed.
   12792             :  *
   12793             :  * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
   12794             :  *
   12795             :  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
   12796             :  */
   12797          79 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
   12798             : {
   12799          79 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12800          79 :     return hArray->m_poImpl->GetRawNoDataValue();
   12801             : }
   12802             : 
   12803             : /************************************************************************/
   12804             : /*                 GDALMDArrayGetNoDataValueAsDouble()                  */
   12805             : /************************************************************************/
   12806             : 
   12807             : /** Return the nodata value as a double.
   12808             :  *
   12809             :  * The value returned might be nullptr in case of no nodata value. When
   12810             :  * a nodata value is registered, a non-nullptr will be returned whose size in
   12811             :  * bytes is GetDataType().GetSize().
   12812             :  *
   12813             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
   12814             :  *
   12815             :  * @param hArray Array handle.
   12816             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12817             :  * if a nodata value exists and can be converted to double. Might be nullptr.
   12818             :  *
   12819             :  * @return the nodata value as a double. A 0.0 value might also indicate the
   12820             :  * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
   12821             :  * will be set to false then).
   12822             :  */
   12823         124 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
   12824             :                                          int *pbHasNoDataValue)
   12825             : {
   12826         124 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12827         124 :     bool bHasNodataValue = false;
   12828         124 :     double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
   12829         124 :     if (pbHasNoDataValue)
   12830         124 :         *pbHasNoDataValue = bHasNodataValue;
   12831         124 :     return ret;
   12832             : }
   12833             : 
   12834             : /************************************************************************/
   12835             : /*                  GDALMDArrayGetNoDataValueAsInt64()                  */
   12836             : /************************************************************************/
   12837             : 
   12838             : /** Return the nodata value as a Int64.
   12839             :  *
   12840             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
   12841             :  *
   12842             :  * @param hArray Array handle.
   12843             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12844             :  * if a nodata value exists and can be converted to Int64. Might be nullptr.
   12845             :  *
   12846             :  * @return the nodata value as a Int64.
   12847             :  * @since GDAL 3.5
   12848             :  */
   12849          13 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
   12850             :                                          int *pbHasNoDataValue)
   12851             : {
   12852          13 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12853          13 :     bool bHasNodataValue = false;
   12854          13 :     const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
   12855          13 :     if (pbHasNoDataValue)
   12856          13 :         *pbHasNoDataValue = bHasNodataValue;
   12857          13 :     return ret;
   12858             : }
   12859             : 
   12860             : /************************************************************************/
   12861             : /*                 GDALMDArrayGetNoDataValueAsUInt64()                  */
   12862             : /************************************************************************/
   12863             : 
   12864             : /** Return the nodata value as a UInt64.
   12865             :  *
   12866             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
   12867             :  *
   12868             :  * @param hArray Array handle.
   12869             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12870             :  * if a nodata value exists and can be converted to UInt64. Might be nullptr.
   12871             :  *
   12872             :  * @return the nodata value as a UInt64.
   12873             :  * @since GDAL 3.5
   12874             :  */
   12875           7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
   12876             :                                            int *pbHasNoDataValue)
   12877             : {
   12878           7 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12879           7 :     bool bHasNodataValue = false;
   12880           7 :     const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
   12881           7 :     if (pbHasNoDataValue)
   12882           7 :         *pbHasNoDataValue = bHasNodataValue;
   12883           7 :     return ret;
   12884             : }
   12885             : 
   12886             : /************************************************************************/
   12887             : /*                    GDALMDArraySetRawNoDataValue()                    */
   12888             : /************************************************************************/
   12889             : 
   12890             : /** Set the nodata value as a "raw" value.
   12891             :  *
   12892             :  * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
   12893             :  * void*).
   12894             :  *
   12895             :  * @return TRUE in case of success.
   12896             :  */
   12897          15 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
   12898             : {
   12899          15 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12900          15 :     return hArray->m_poImpl->SetRawNoDataValue(pNoData);
   12901             : }
   12902             : 
   12903             : /************************************************************************/
   12904             : /*                 GDALMDArraySetNoDataValueAsDouble()                  */
   12905             : /************************************************************************/
   12906             : 
   12907             : /** Set the nodata value as a double.
   12908             :  *
   12909             :  * If the natural data type of the attribute/array is not double, type
   12910             :  * conversion will occur to the type returned by GetDataType().
   12911             :  *
   12912             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
   12913             :  *
   12914             :  * @return TRUE in case of success.
   12915             :  */
   12916          58 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
   12917             : {
   12918          58 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12919          58 :     return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
   12920             : }
   12921             : 
   12922             : /************************************************************************/
   12923             : /*                  GDALMDArraySetNoDataValueAsInt64()                  */
   12924             : /************************************************************************/
   12925             : 
   12926             : /** Set the nodata value as a Int64.
   12927             :  *
   12928             :  * If the natural data type of the attribute/array is not Int64, type conversion
   12929             :  * will occur to the type returned by GetDataType().
   12930             :  *
   12931             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
   12932             :  *
   12933             :  * @return TRUE in case of success.
   12934             :  * @since GDAL 3.5
   12935             :  */
   12936           1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
   12937             : {
   12938           1 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12939           1 :     return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
   12940             : }
   12941             : 
   12942             : /************************************************************************/
   12943             : /*                 GDALMDArraySetNoDataValueAsUInt64()                  */
   12944             : /************************************************************************/
   12945             : 
   12946             : /** Set the nodata value as a UInt64.
   12947             :  *
   12948             :  * If the natural data type of the attribute/array is not UInt64, type
   12949             :  * conversion will occur to the type returned by GetDataType().
   12950             :  *
   12951             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
   12952             :  *
   12953             :  * @return TRUE in case of success.
   12954             :  * @since GDAL 3.5
   12955             :  */
   12956           1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
   12957             :                                       uint64_t nNoDataValue)
   12958             : {
   12959           1 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12960           1 :     return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
   12961             : }
   12962             : 
   12963             : /************************************************************************/
   12964             : /*                         GDALMDArrayResize()                          */
   12965             : /************************************************************************/
   12966             : 
   12967             : /** Resize an array to new dimensions.
   12968             :  *
   12969             :  * Not all drivers may allow this operation, and with restrictions (e.g.
   12970             :  * for netCDF, this is limited to growing of "unlimited" dimensions)
   12971             :  *
   12972             :  * Resizing a dimension used in other arrays will cause those other arrays
   12973             :  * to be resized.
   12974             :  *
   12975             :  * This is the same as the C++ method GDALMDArray::Resize().
   12976             :  *
   12977             :  * @param hArray Array.
   12978             :  * @param panNewDimSizes Array of GetDimensionCount() values containing the
   12979             :  *                       new size of each indexing dimension.
   12980             :  * @param papszOptions Options. (Driver specific)
   12981             :  * @return true in case of success.
   12982             :  * @since GDAL 3.7
   12983             :  */
   12984          42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
   12985             :                        CSLConstList papszOptions)
   12986             : {
   12987          42 :     VALIDATE_POINTER1(hArray, __func__, false);
   12988          42 :     VALIDATE_POINTER1(panNewDimSizes, __func__, false);
   12989          84 :     std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
   12990         125 :     for (size_t i = 0; i < anNewDimSizes.size(); ++i)
   12991             :     {
   12992          83 :         anNewDimSizes[i] = panNewDimSizes[i];
   12993             :     }
   12994          42 :     return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
   12995             : }
   12996             : 
   12997             : /************************************************************************/
   12998             : /*                        GDALMDArraySetScale()                         */
   12999             : /************************************************************************/
   13000             : 
   13001             : /** Set the scale value to apply to raw values.
   13002             :  *
   13003             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   13004             :  *
   13005             :  * This is the same as the C++ method GDALMDArray::SetScale().
   13006             :  *
   13007             :  * @return TRUE in case of success.
   13008             :  */
   13009           0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
   13010             : {
   13011           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13012           0 :     return hArray->m_poImpl->SetScale(dfScale);
   13013             : }
   13014             : 
   13015             : /************************************************************************/
   13016             : /*                       GDALMDArraySetScaleEx()                        */
   13017             : /************************************************************************/
   13018             : 
   13019             : /** Set the scale value to apply to raw values.
   13020             :  *
   13021             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   13022             :  *
   13023             :  * This is the same as the C++ method GDALMDArray::SetScale().
   13024             :  *
   13025             :  * @return TRUE in case of success.
   13026             :  * @since GDAL 3.3
   13027             :  */
   13028          21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
   13029             :                           GDALDataType eStorageType)
   13030             : {
   13031          21 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13032          21 :     return hArray->m_poImpl->SetScale(dfScale, eStorageType);
   13033             : }
   13034             : 
   13035             : /************************************************************************/
   13036             : /*                        GDALMDArraySetOffset()                        */
   13037             : /************************************************************************/
   13038             : 
   13039             : /** Set the scale value to apply to raw values.
   13040             :  *
   13041             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   13042             :  *
   13043             :  * This is the same as the C++ method GDALMDArray::SetOffset().
   13044             :  *
   13045             :  * @return TRUE in case of success.
   13046             :  */
   13047           0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
   13048             : {
   13049           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13050           0 :     return hArray->m_poImpl->SetOffset(dfOffset);
   13051             : }
   13052             : 
   13053             : /************************************************************************/
   13054             : /*                       GDALMDArraySetOffsetEx()                       */
   13055             : /************************************************************************/
   13056             : 
   13057             : /** Set the scale value to apply to raw values.
   13058             :  *
   13059             :  * unscaled_value = raw_value * GetOffset() + GetOffset()
   13060             :  *
   13061             :  * This is the same as the C++ method GDALMDArray::SetOffset().
   13062             :  *
   13063             :  * @return TRUE in case of success.
   13064             :  * @since GDAL 3.3
   13065             :  */
   13066          21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
   13067             :                            GDALDataType eStorageType)
   13068             : {
   13069          21 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13070          21 :     return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
   13071             : }
   13072             : 
   13073             : /************************************************************************/
   13074             : /*                        GDALMDArrayGetScale()                         */
   13075             : /************************************************************************/
   13076             : 
   13077             : /** Get the scale value to apply to raw values.
   13078             :  *
   13079             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   13080             :  *
   13081             :  * This is the same as the C++ method GDALMDArray::GetScale().
   13082             :  *
   13083             :  * @return the scale value
   13084             :  */
   13085         105 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
   13086             : {
   13087         105 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   13088         105 :     bool bHasValue = false;
   13089         105 :     double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
   13090         105 :     if (pbHasValue)
   13091         105 :         *pbHasValue = bHasValue;
   13092         105 :     return dfRet;
   13093             : }
   13094             : 
   13095             : /************************************************************************/
   13096             : /*                       GDALMDArrayGetScaleEx()                        */
   13097             : /************************************************************************/
   13098             : 
   13099             : /** Get the scale value to apply to raw values.
   13100             :  *
   13101             :  * unscaled_value = raw_value * GetScale() + GetScale()
   13102             :  *
   13103             :  * This is the same as the C++ method GDALMDArray::GetScale().
   13104             :  *
   13105             :  * @return the scale value
   13106             :  * @since GDAL 3.3
   13107             :  */
   13108           5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
   13109             :                              GDALDataType *peStorageType)
   13110             : {
   13111           5 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   13112           5 :     bool bHasValue = false;
   13113           5 :     double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
   13114           5 :     if (pbHasValue)
   13115           5 :         *pbHasValue = bHasValue;
   13116           5 :     return dfRet;
   13117             : }
   13118             : 
   13119             : /************************************************************************/
   13120             : /*                        GDALMDArrayGetOffset()                        */
   13121             : /************************************************************************/
   13122             : 
   13123             : /** Get the scale value to apply to raw values.
   13124             :  *
   13125             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   13126             :  *
   13127             :  * This is the same as the C++ method GDALMDArray::GetOffset().
   13128             :  *
   13129             :  * @return the scale value
   13130             :  */
   13131         102 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
   13132             : {
   13133         102 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   13134         102 :     bool bHasValue = false;
   13135         102 :     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
   13136         102 :     if (pbHasValue)
   13137         102 :         *pbHasValue = bHasValue;
   13138         102 :     return dfRet;
   13139             : }
   13140             : 
   13141             : /************************************************************************/
   13142             : /*                       GDALMDArrayGetOffsetEx()                       */
   13143             : /************************************************************************/
   13144             : 
   13145             : /** Get the scale value to apply to raw values.
   13146             :  *
   13147             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   13148             :  *
   13149             :  * This is the same as the C++ method GDALMDArray::GetOffset().
   13150             :  *
   13151             :  * @return the scale value
   13152             :  * @since GDAL 3.3
   13153             :  */
   13154           5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
   13155             :                               GDALDataType *peStorageType)
   13156             : {
   13157           5 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   13158           5 :     bool bHasValue = false;
   13159           5 :     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
   13160           5 :     if (pbHasValue)
   13161           5 :         *pbHasValue = bHasValue;
   13162           5 :     return dfRet;
   13163             : }
   13164             : 
   13165             : /************************************************************************/
   13166             : /*                      GDALMDArrayGetBlockSize()                       */
   13167             : /************************************************************************/
   13168             : 
   13169             : /** Return the "natural" block size of the array along all dimensions.
   13170             :  *
   13171             :  * Some drivers might organize the array in tiles/blocks and reading/writing
   13172             :  * aligned on those tile/block boundaries will be more efficient.
   13173             :  *
   13174             :  * The returned number of elements in the vector is the same as
   13175             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
   13176             :  * the natural block size along the considered dimension.
   13177             :  * "Flat" arrays will typically return a vector of values set to 0.
   13178             :  *
   13179             :  * The default implementation will return a vector of values set to 0.
   13180             :  *
   13181             :  * This method is used by GetProcessingChunkSize().
   13182             :  *
   13183             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikely
   13184             :  * theoretical case of a 32-bit platform, this might exceed its size_t
   13185             :  * allocation capabilities.
   13186             :  *
   13187             :  * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
   13188             :  *
   13189             :  * @return the block size, in number of elements along each dimension.
   13190             :  */
   13191         110 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
   13192             : {
   13193         110 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13194         110 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13195         110 :     auto res = hArray->m_poImpl->GetBlockSize();
   13196         110 :     auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
   13197         339 :     for (size_t i = 0; i < res.size(); i++)
   13198             :     {
   13199         229 :         ret[i] = res[i];
   13200             :     }
   13201         110 :     *pnCount = res.size();
   13202         110 :     return ret;
   13203             : }
   13204             : 
   13205             : /************************************************************************/
   13206             : /*                 GDALMDArrayGetProcessingChunkSize()                  */
   13207             : /************************************************************************/
   13208             : 
   13209             : /** \brief Return an optimal chunk size for read/write operations, given the
   13210             :  * natural block size and memory constraints specified.
   13211             :  *
   13212             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
   13213             :  * multiple of those returned by GetBlockSize() (unless the block define by
   13214             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
   13215             :  * returned by this method).
   13216             :  *
   13217             :  * This is the same as the C++ method
   13218             :  * GDALAbstractMDArray::GetProcessingChunkSize().
   13219             :  *
   13220             :  * @param hArray Array.
   13221             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13222             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
   13223             :  * chunk.
   13224             :  *
   13225             :  * @return the chunk size, in number of elements along each dimension.
   13226             :  */
   13227             : 
   13228           1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
   13229             :                                           size_t nMaxChunkMemory)
   13230             : {
   13231           1 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13232           1 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13233           1 :     auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
   13234           1 :     auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
   13235           3 :     for (size_t i = 0; i < res.size(); i++)
   13236             :     {
   13237           2 :         ret[i] = res[i];
   13238             :     }
   13239           1 :     *pnCount = res.size();
   13240           1 :     return ret;
   13241             : }
   13242             : 
   13243             : /************************************************************************/
   13244             : /*                    GDALMDArrayGetStructuralInfo()                    */
   13245             : /************************************************************************/
   13246             : 
   13247             : /** Return structural information on the array.
   13248             :  *
   13249             :  * This may be the compression, etc..
   13250             :  *
   13251             :  * The return value should not be freed and is valid until GDALMDArray is
   13252             :  * released or this function called again.
   13253             :  *
   13254             :  * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
   13255             :  */
   13256          17 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
   13257             : {
   13258          17 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13259          17 :     return hArray->m_poImpl->GetStructuralInfo();
   13260             : }
   13261             : 
   13262             : /************************************************************************/
   13263             : /*                         GDALMDArrayGetView()                         */
   13264             : /************************************************************************/
   13265             : 
   13266             : /** Return a view of the array using slicing or field access.
   13267             :  *
   13268             :  * The returned object should be released with GDALMDArrayRelease().
   13269             :  *
   13270             :  * This is the same as the C++ method GDALMDArray::GetView().
   13271             :  */
   13272         438 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
   13273             : {
   13274         438 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13275         438 :     VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
   13276        1314 :     auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
   13277         438 :     if (!sliced)
   13278          23 :         return nullptr;
   13279         415 :     return new GDALMDArrayHS(sliced);
   13280             : }
   13281             : 
   13282             : /************************************************************************/
   13283             : /*                        GDALMDArrayTranspose()                        */
   13284             : /************************************************************************/
   13285             : 
   13286             : /** Return a view of the array whose axis have been reordered.
   13287             :  *
   13288             :  * The returned object should be released with GDALMDArrayRelease().
   13289             :  *
   13290             :  * This is the same as the C++ method GDALMDArray::Transpose().
   13291             :  */
   13292          44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
   13293             :                                   const int *panMapNewAxisToOldAxis)
   13294             : {
   13295          44 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13296          88 :     std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
   13297          44 :     if (nNewAxisCount)
   13298             :     {
   13299          43 :         memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
   13300             :                nNewAxisCount * sizeof(int));
   13301             :     }
   13302          88 :     auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
   13303          44 :     if (!reordered)
   13304           7 :         return nullptr;
   13305          37 :     return new GDALMDArrayHS(reordered);
   13306             : }
   13307             : 
   13308             : /************************************************************************/
   13309             : /*                       GDALMDArrayGetUnscaled()                       */
   13310             : /************************************************************************/
   13311             : 
   13312             : /** Return an array that is the unscaled version of the current one.
   13313             :  *
   13314             :  * That is each value of the unscaled array will be
   13315             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   13316             :  *
   13317             :  * Starting with GDAL 3.3, the Write() method is implemented and will convert
   13318             :  * from unscaled values to raw values.
   13319             :  *
   13320             :  * The returned object should be released with GDALMDArrayRelease().
   13321             :  *
   13322             :  * This is the same as the C++ method GDALMDArray::GetUnscaled().
   13323             :  */
   13324          13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
   13325             : {
   13326          13 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13327          26 :     auto unscaled = hArray->m_poImpl->GetUnscaled();
   13328          13 :     if (!unscaled)
   13329           0 :         return nullptr;
   13330          13 :     return new GDALMDArrayHS(unscaled);
   13331             : }
   13332             : 
   13333             : /************************************************************************/
   13334             : /*                         GDALMDArrayGetMask()                         */
   13335             : /************************************************************************/
   13336             : 
   13337             : /** Return an array that is a mask for the current array
   13338             :  *
   13339             :  * This array will be of type Byte, with values set to 0 to indicate invalid
   13340             :  * pixels of the current array, and values set to 1 to indicate valid pixels.
   13341             :  *
   13342             :  * The returned object should be released with GDALMDArrayRelease().
   13343             :  *
   13344             :  * This is the same as the C++ method GDALMDArray::GetMask().
   13345             :  */
   13346          35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
   13347             : {
   13348          35 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13349          70 :     auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
   13350          35 :     if (!unscaled)
   13351           7 :         return nullptr;
   13352          28 :     return new GDALMDArrayHS(unscaled);
   13353             : }
   13354             : 
   13355             : /************************************************************************/
   13356             : /*                      GDALMDArrayGetResampled()                       */
   13357             : /************************************************************************/
   13358             : 
   13359             : /** Return an array that is a resampled / reprojected view of the current array
   13360             :  *
   13361             :  * This is the same as the C++ method GDALMDArray::GetResampled().
   13362             :  *
   13363             :  * Currently this method can only resample along the last 2 dimensions, unless
   13364             :  * orthorectifying a NASA EMIT dataset.
   13365             :  *
   13366             :  * The returned object should be released with GDALMDArrayRelease().
   13367             :  *
   13368             :  * @since 3.4
   13369             :  */
   13370          34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
   13371             :                                      const GDALDimensionH *pahNewDims,
   13372             :                                      GDALRIOResampleAlg resampleAlg,
   13373             :                                      OGRSpatialReferenceH hTargetSRS,
   13374             :                                      CSLConstList papszOptions)
   13375             : {
   13376          34 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13377          34 :     VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
   13378          68 :     std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
   13379         112 :     for (size_t i = 0; i < nNewDimCount; ++i)
   13380             :     {
   13381          78 :         if (pahNewDims[i])
   13382           8 :             apoNewDims[i] = pahNewDims[i]->m_poImpl;
   13383             :     }
   13384          34 :     auto poNewArray = hArray->m_poImpl->GetResampled(
   13385          34 :         apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
   13386          68 :         papszOptions);
   13387          34 :     if (!poNewArray)
   13388           8 :         return nullptr;
   13389          26 :     return new GDALMDArrayHS(poNewArray);
   13390             : }
   13391             : 
   13392             : /************************************************************************/
   13393             : /*                         GDALMDArraySetUnit()                         */
   13394             : /************************************************************************/
   13395             : 
   13396             : /** Set the variable unit.
   13397             :  *
   13398             :  * Values should conform as much as possible with those allowed by
   13399             :  * the NetCDF CF conventions:
   13400             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
   13401             :  * but others might be returned.
   13402             :  *
   13403             :  * Few examples are "meter", "degrees", "second", ...
   13404             :  * Empty value means unknown.
   13405             :  *
   13406             :  * This is the same as the C function GDALMDArraySetUnit()
   13407             :  *
   13408             :  * @param hArray array.
   13409             :  * @param pszUnit unit name.
   13410             :  * @return TRUE in case of success.
   13411             :  */
   13412          15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
   13413             : {
   13414          15 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13415          15 :     return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
   13416             : }
   13417             : 
   13418             : /************************************************************************/
   13419             : /*                         GDALMDArrayGetUnit()                         */
   13420             : /************************************************************************/
   13421             : 
   13422             : /** Return the array unit.
   13423             :  *
   13424             :  * Values should conform as much as possible with those allowed by
   13425             :  * the NetCDF CF conventions:
   13426             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
   13427             :  * but others might be returned.
   13428             :  *
   13429             :  * Few examples are "meter", "degrees", "second", ...
   13430             :  * Empty value means unknown.
   13431             :  *
   13432             :  * The return value should not be freed and is valid until GDALMDArray is
   13433             :  * released or this function called again.
   13434             :  *
   13435             :  * This is the same as the C++ method GDALMDArray::GetUnit().
   13436             :  */
   13437         113 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
   13438             : {
   13439         113 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13440         113 :     return hArray->m_poImpl->GetUnit().c_str();
   13441             : }
   13442             : 
   13443             : /************************************************************************/
   13444             : /*                      GDALMDArrayGetSpatialRef()                      */
   13445             : /************************************************************************/
   13446             : 
   13447             : /** Assign a spatial reference system object to the array.
   13448             :  *
   13449             :  * This is the same as the C++ method GDALMDArray::SetSpatialRef().
   13450             :  * @return TRUE in case of success.
   13451             :  */
   13452          30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
   13453             : {
   13454          30 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13455          60 :     return hArray->m_poImpl->SetSpatialRef(
   13456          60 :         OGRSpatialReference::FromHandle(hSRS));
   13457             : }
   13458             : 
   13459             : /************************************************************************/
   13460             : /*                      GDALMDArrayGetSpatialRef()                      */
   13461             : /************************************************************************/
   13462             : 
   13463             : /** Return the spatial reference system object associated with the array.
   13464             :  *
   13465             :  * This is the same as the C++ method GDALMDArray::GetSpatialRef().
   13466             :  *
   13467             :  * The returned object must be freed with OSRDestroySpatialReference().
   13468             :  */
   13469          81 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
   13470             : {
   13471          81 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13472          81 :     auto poSRS = hArray->m_poImpl->GetSpatialRef();
   13473          81 :     return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
   13474             : }
   13475             : 
   13476             : /************************************************************************/
   13477             : /*                      GDALMDArrayGetStatistics()                      */
   13478             : /************************************************************************/
   13479             : 
   13480             : /**
   13481             :  * \brief Fetch statistics.
   13482             :  *
   13483             :  * This is the same as the C++ method GDALMDArray::GetStatistics().
   13484             :  *
   13485             :  * @since GDAL 3.2
   13486             :  */
   13487             : 
   13488          15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
   13489             :                                 int bApproxOK, int bForce, double *pdfMin,
   13490             :                                 double *pdfMax, double *pdfMean,
   13491             :                                 double *pdfStdDev, GUInt64 *pnValidCount,
   13492             :                                 GDALProgressFunc pfnProgress,
   13493             :                                 void *pProgressData)
   13494             : {
   13495          15 :     VALIDATE_POINTER1(hArray, __func__, CE_Failure);
   13496          30 :     return hArray->m_poImpl->GetStatistics(
   13497          15 :         CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
   13498          15 :         pdfStdDev, pnValidCount, pfnProgress, pProgressData);
   13499             : }
   13500             : 
   13501             : /************************************************************************/
   13502             : /*                    GDALMDArrayComputeStatistics()                    */
   13503             : /************************************************************************/
   13504             : 
   13505             : /**
   13506             :  * \brief Compute statistics.
   13507             :  *
   13508             :  * This is the same as the C++ method GDALMDArray::ComputeStatistics().
   13509             :  *
   13510             :  * @since GDAL 3.2
   13511             :  * @see GDALMDArrayComputeStatisticsEx()
   13512             :  */
   13513             : 
   13514           0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
   13515             :                                  int bApproxOK, double *pdfMin, double *pdfMax,
   13516             :                                  double *pdfMean, double *pdfStdDev,
   13517             :                                  GUInt64 *pnValidCount,
   13518             :                                  GDALProgressFunc pfnProgress,
   13519             :                                  void *pProgressData)
   13520             : {
   13521           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13522           0 :     return hArray->m_poImpl->ComputeStatistics(
   13523           0 :         CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
   13524           0 :         pnValidCount, pfnProgress, pProgressData, nullptr);
   13525             : }
   13526             : 
   13527             : /************************************************************************/
   13528             : /*                   GDALMDArrayComputeStatisticsEx()                   */
   13529             : /************************************************************************/
   13530             : 
   13531             : /**
   13532             :  * \brief Compute statistics.
   13533             :  *
   13534             :  * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
   13535             :  *
   13536             :  * This is the same as the C++ method GDALMDArray::ComputeStatistics().
   13537             :  *
   13538             :  * @since GDAL 3.8
   13539             :  */
   13540             : 
   13541           4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
   13542             :                                    int bApproxOK, double *pdfMin,
   13543             :                                    double *pdfMax, double *pdfMean,
   13544             :                                    double *pdfStdDev, GUInt64 *pnValidCount,
   13545             :                                    GDALProgressFunc pfnProgress,
   13546             :                                    void *pProgressData,
   13547             :                                    CSLConstList papszOptions)
   13548             : {
   13549           4 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13550           8 :     return hArray->m_poImpl->ComputeStatistics(
   13551           4 :         CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
   13552           8 :         pnValidCount, pfnProgress, pProgressData, papszOptions);
   13553             : }
   13554             : 
   13555             : /************************************************************************/
   13556             : /*                 GDALMDArrayGetCoordinateVariables()                  */
   13557             : /************************************************************************/
   13558             : 
   13559             : /** Return coordinate variables.
   13560             :  *
   13561             :  * The returned array must be freed with GDALReleaseArrays(). If only the array
   13562             :  * itself needs to be freed, CPLFree() should be called (and
   13563             :  * GDALMDArrayRelease() on individual array members).
   13564             :  *
   13565             :  * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
   13566             :  *
   13567             :  * @param hArray Array.
   13568             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13569             :  *
   13570             :  * @return an array of *pnCount arrays.
   13571             :  * @since 3.4
   13572             :  */
   13573          13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
   13574             :                                                 size_t *pnCount)
   13575             : {
   13576          13 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13577          13 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13578          13 :     const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
   13579             :     auto ret = static_cast<GDALMDArrayH *>(
   13580          13 :         CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
   13581          29 :     for (size_t i = 0; i < coordinates.size(); i++)
   13582             :     {
   13583          16 :         ret[i] = new GDALMDArrayHS(coordinates[i]);
   13584             :     }
   13585          13 :     *pnCount = coordinates.size();
   13586          13 :     return ret;
   13587             : }
   13588             : 
   13589             : /************************************************************************/
   13590             : /*                       GDALMDArrayGetGridded()                        */
   13591             : /************************************************************************/
   13592             : 
   13593             : /** Return a gridded array from scattered point data, that is from an array
   13594             :  * whose last dimension is the indexing variable of X and Y arrays.
   13595             :  *
   13596             :  * The returned object should be released with GDALMDArrayRelease().
   13597             :  *
   13598             :  * This is the same as the C++ method GDALMDArray::GetGridded().
   13599             :  *
   13600             :  * @since GDAL 3.7
   13601             :  */
   13602          22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
   13603             :                                    const char *pszGridOptions,
   13604             :                                    GDALMDArrayH hXArray, GDALMDArrayH hYArray,
   13605             :                                    CSLConstList papszOptions)
   13606             : {
   13607          22 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13608          22 :     VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
   13609          22 :     auto gridded = hArray->m_poImpl->GetGridded(
   13610          44 :         pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
   13611          88 :         hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
   13612          22 :     if (!gridded)
   13613          19 :         return nullptr;
   13614           3 :     return new GDALMDArrayHS(gridded);
   13615             : }
   13616             : 
   13617             : /************************************************************************/
   13618             : /*                       GDALMDArrayGetMeshGrid()                       */
   13619             : /************************************************************************/
   13620             : 
   13621             : /** Return a list of multidimensional arrays from a list of one-dimensional
   13622             :  * arrays.
   13623             :  *
   13624             :  * This is typically used to transform one-dimensional longitude, latitude
   13625             :  * arrays into 2D ones.
   13626             :  *
   13627             :  * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
   13628             :  * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
   13629             :  * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
   13630             :  * repeated to fill the matrix along the first dimension for x1, the second
   13631             :  * for x2 and so on.
   13632             :  *
   13633             :  * For example, if x = [1, 2], and y = [3, 4, 5],
   13634             :  * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
   13635             :  * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
   13636             :  * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
   13637             :  *
   13638             :  * and
   13639             :  * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
   13640             :  * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
   13641             :  * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
   13642             :  *
   13643             :  * The currently supported options are:
   13644             :  * <ul>
   13645             :  * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
   13646             :  * output.
   13647             :  * </li>
   13648             :  * </ul>
   13649             :  *
   13650             :  * This is the same as
   13651             :  * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
   13652             :  * function.
   13653             :  *
   13654             :  * The returned array (of arrays) must be freed with GDALReleaseArrays().
   13655             :  * If only the array itself needs to be freed, CPLFree() should be called
   13656             :  * (and GDALMDArrayRelease() on individual array members).
   13657             :  *
   13658             :  * This is the same as the C++ method GDALMDArray::GetMeshGrid()
   13659             :  *
   13660             :  * @param pahInputArrays Input arrays
   13661             :  * @param nCountInputArrays Number of input arrays
   13662             :  * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
   13663             :  * @param papszOptions NULL, or NULL terminated list of options.
   13664             :  *
   13665             :  * @return an array of *pnCountOutputArrays arrays.
   13666             :  * @since 3.10
   13667             :  */
   13668           7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
   13669             :                                      size_t nCountInputArrays,
   13670             :                                      size_t *pnCountOutputArrays,
   13671             :                                      CSLConstList papszOptions)
   13672             : {
   13673           7 :     VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
   13674           7 :     VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
   13675             : 
   13676          14 :     std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
   13677          20 :     for (size_t i = 0; i < nCountInputArrays; ++i)
   13678          13 :         apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
   13679             : 
   13680             :     const auto apoOutputArrays =
   13681           7 :         GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
   13682             :     auto ret = static_cast<GDALMDArrayH *>(
   13683           7 :         CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
   13684          17 :     for (size_t i = 0; i < apoOutputArrays.size(); i++)
   13685             :     {
   13686          10 :         ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
   13687             :     }
   13688           7 :     *pnCountOutputArrays = apoOutputArrays.size();
   13689           7 :     return ret;
   13690             : }
   13691             : 
   13692             : /************************************************************************/
   13693             : /*                         GDALReleaseArrays()                          */
   13694             : /************************************************************************/
   13695             : 
   13696             : /** Free the return of GDALMDArrayGetCoordinateVariables()
   13697             :  *
   13698             :  * @param arrays return pointer of above methods
   13699             :  * @param nCount *pnCount value returned by above methods
   13700             :  */
   13701          20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
   13702             : {
   13703          46 :     for (size_t i = 0; i < nCount; i++)
   13704             :     {
   13705          26 :         delete arrays[i];
   13706             :     }
   13707          20 :     CPLFree(arrays);
   13708          20 : }
   13709             : 
   13710             : /************************************************************************/
   13711             : /*                          GDALMDArrayCache()                          */
   13712             : /************************************************************************/
   13713             : 
   13714             : /**
   13715             :  * \brief Cache the content of the array into an auxiliary filename.
   13716             :  *
   13717             :  * This is the same as the C++ method GDALMDArray::Cache().
   13718             :  *
   13719             :  * @since GDAL 3.4
   13720             :  */
   13721             : 
   13722           7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
   13723             : {
   13724           7 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13725           7 :     return hArray->m_poImpl->Cache(papszOptions);
   13726             : }
   13727             : 
   13728             : /************************************************************************/
   13729             : /*                         GDALMDArrayRename()                          */
   13730             : /************************************************************************/
   13731             : 
   13732             : /** Rename the array.
   13733             :  *
   13734             :  * This is not implemented by all drivers.
   13735             :  *
   13736             :  * Drivers known to implement it: MEM, netCDF, Zarr.
   13737             :  *
   13738             :  * This is the same as the C++ method GDALAbstractMDArray::Rename()
   13739             :  *
   13740             :  * @return true in case of success
   13741             :  * @since GDAL 3.8
   13742             :  */
   13743          28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
   13744             : {
   13745          28 :     VALIDATE_POINTER1(hArray, __func__, false);
   13746          28 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   13747          28 :     return hArray->m_poImpl->Rename(pszNewName);
   13748             : }
   13749             : 
   13750             : /************************************************************************/
   13751             : /*                        GDALAttributeRelease()                        */
   13752             : /************************************************************************/
   13753             : 
   13754             : /** Release the GDAL in-memory object associated with a GDALAttribute.
   13755             :  *
   13756             :  * Note: when applied on a object coming from a driver, this does not
   13757             :  * destroy the object in the file, database, etc...
   13758             :  */
   13759         818 : void GDALAttributeRelease(GDALAttributeH hAttr)
   13760             : {
   13761         818 :     delete hAttr;
   13762         818 : }
   13763             : 
   13764             : /************************************************************************/
   13765             : /*                        GDALAttributeGetName()                        */
   13766             : /************************************************************************/
   13767             : 
   13768             : /** Return the name of the attribute.
   13769             :  *
   13770             :  * The returned pointer is valid until hAttr is released.
   13771             :  *
   13772             :  * This is the same as the C++ method GDALAttribute::GetName().
   13773             :  */
   13774         361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
   13775             : {
   13776         361 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13777         361 :     return hAttr->m_poImpl->GetName().c_str();
   13778             : }
   13779             : 
   13780             : /************************************************************************/
   13781             : /*                      GDALAttributeGetFullName()                      */
   13782             : /************************************************************************/
   13783             : 
   13784             : /** Return the full name of the attribute.
   13785             :  *
   13786             :  * The returned pointer is valid until hAttr is released.
   13787             :  *
   13788             :  * This is the same as the C++ method GDALAttribute::GetFullName().
   13789             :  */
   13790          50 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
   13791             : {
   13792          50 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13793          50 :     return hAttr->m_poImpl->GetFullName().c_str();
   13794             : }
   13795             : 
   13796             : /************************************************************************/
   13797             : /*                 GDALAttributeGetTotalElementsCount()                 */
   13798             : /************************************************************************/
   13799             : 
   13800             : /** Return the total number of values in the attribute.
   13801             :  *
   13802             :  * This is the same as the C++ method
   13803             :  * GDALAbstractMDArray::GetTotalElementsCount()
   13804             :  */
   13805         190 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
   13806             : {
   13807         190 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13808         190 :     return hAttr->m_poImpl->GetTotalElementsCount();
   13809             : }
   13810             : 
   13811             : /************************************************************************/
   13812             : /*                   GDALAttributeGetDimensionCount()                   */
   13813             : /************************************************************************/
   13814             : 
   13815             : /** Return the number of dimensions.
   13816             :  *
   13817             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
   13818             :  */
   13819          12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
   13820             : {
   13821          12 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13822          12 :     return hAttr->m_poImpl->GetDimensionCount();
   13823             : }
   13824             : 
   13825             : /************************************************************************/
   13826             : /*                   GDALAttributeGetDimensionsSize()                   */
   13827             : /************************************************************************/
   13828             : 
   13829             : /** Return the dimension sizes of the attribute.
   13830             :  *
   13831             :  * The returned array must be freed with CPLFree()
   13832             :  *
   13833             :  * @param hAttr Attribute.
   13834             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13835             :  *
   13836             :  * @return an array of *pnCount values.
   13837             :  */
   13838          11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
   13839             : {
   13840          11 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13841          11 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13842          11 :     const auto &dims = hAttr->m_poImpl->GetDimensions();
   13843          11 :     auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
   13844          22 :     for (size_t i = 0; i < dims.size(); i++)
   13845             :     {
   13846          11 :         ret[i] = dims[i]->GetSize();
   13847             :     }
   13848          11 :     *pnCount = dims.size();
   13849          11 :     return ret;
   13850             : }
   13851             : 
   13852             : /************************************************************************/
   13853             : /*                      GDALAttributeGetDataType()                      */
   13854             : /************************************************************************/
   13855             : 
   13856             : /** Return the data type
   13857             :  *
   13858             :  * The return must be freed with GDALExtendedDataTypeRelease().
   13859             :  */
   13860         483 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
   13861             : {
   13862         483 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13863             :     return new GDALExtendedDataTypeHS(
   13864         483 :         new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
   13865             : }
   13866             : 
   13867             : /************************************************************************/
   13868             : /*                       GDALAttributeReadAsRaw()                       */
   13869             : /************************************************************************/
   13870             : 
   13871             : /** Return the raw value of an attribute.
   13872             :  *
   13873             :  * This is the same as the C++ method GDALAttribute::ReadAsRaw().
   13874             :  *
   13875             :  * The returned buffer must be freed with GDALAttributeFreeRawResult()
   13876             :  *
   13877             :  * @param hAttr Attribute.
   13878             :  * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
   13879             :  *
   13880             :  * @return a buffer of *pnSize bytes.
   13881             :  */
   13882           6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
   13883             : {
   13884           6 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13885           6 :     VALIDATE_POINTER1(pnSize, __func__, nullptr);
   13886          12 :     auto res(hAttr->m_poImpl->ReadAsRaw());
   13887           6 :     *pnSize = res.size();
   13888           6 :     auto ret = res.StealData();
   13889           6 :     if (!ret)
   13890             :     {
   13891           0 :         *pnSize = 0;
   13892           0 :         return nullptr;
   13893             :     }
   13894           6 :     return ret;
   13895             : }
   13896             : 
   13897             : /************************************************************************/
   13898             : /*                     GDALAttributeFreeRawResult()                     */
   13899             : /************************************************************************/
   13900             : 
   13901             : /** Free the return of GDALAttributeAsRaw()
   13902             :  */
   13903           6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
   13904             :                                 CPL_UNUSED size_t nSize)
   13905             : {
   13906           6 :     VALIDATE_POINTER0(hAttr, __func__);
   13907           6 :     if (raw)
   13908             :     {
   13909           6 :         const auto &dt(hAttr->m_poImpl->GetDataType());
   13910           6 :         const auto nDTSize(dt.GetSize());
   13911           6 :         GByte *pabyPtr = raw;
   13912           6 :         const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
   13913           6 :         CPLAssert(nSize == nDTSize * nEltCount);
   13914          12 :         for (size_t i = 0; i < nEltCount; ++i)
   13915             :         {
   13916           6 :             dt.FreeDynamicMemory(pabyPtr);
   13917           6 :             pabyPtr += nDTSize;
   13918             :         }
   13919           6 :         CPLFree(raw);
   13920             :     }
   13921             : }
   13922             : 
   13923             : /************************************************************************/
   13924             : /*                     GDALAttributeReadAsString()                      */
   13925             : /************************************************************************/
   13926             : 
   13927             : /** Return the value of an attribute as a string.
   13928             :  *
   13929             :  * The returned string should not be freed, and its lifetime does not
   13930             :  * excess a next call to ReadAsString() on the same object, or the deletion
   13931             :  * of the object itself.
   13932             :  *
   13933             :  * This function will only return the first element if there are several.
   13934             :  *
   13935             :  * This is the same as the C++ method GDALAttribute::ReadAsString()
   13936             :  *
   13937             :  * @return a string, or nullptr.
   13938             :  */
   13939         115 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
   13940             : {
   13941         115 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13942         115 :     return hAttr->m_poImpl->ReadAsString();
   13943             : }
   13944             : 
   13945             : /************************************************************************/
   13946             : /*                       GDALAttributeReadAsInt()                       */
   13947             : /************************************************************************/
   13948             : 
   13949             : /** Return the value of an attribute as a integer.
   13950             :  *
   13951             :  * This function will only return the first element if there are several.
   13952             :  *
   13953             :  * It can fail if its value can not be converted to integer.
   13954             :  *
   13955             :  * This is the same as the C++ method GDALAttribute::ReadAsInt()
   13956             :  *
   13957             :  * @return a integer, or INT_MIN in case of error.
   13958             :  */
   13959          25 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
   13960             : {
   13961          25 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13962          25 :     return hAttr->m_poImpl->ReadAsInt();
   13963             : }
   13964             : 
   13965             : /************************************************************************/
   13966             : /*                      GDALAttributeReadAsInt64()                      */
   13967             : /************************************************************************/
   13968             : 
   13969             : /** Return the value of an attribute as a int64_t.
   13970             :  *
   13971             :  * This function will only return the first element if there are several.
   13972             :  *
   13973             :  * It can fail if its value can not be converted to integer.
   13974             :  *
   13975             :  * This is the same as the C++ method GDALAttribute::ReadAsInt64()
   13976             :  *
   13977             :  * @return an int64_t, or INT64_MIN in case of error.
   13978             :  */
   13979          27 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
   13980             : {
   13981          27 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13982          27 :     return hAttr->m_poImpl->ReadAsInt64();
   13983             : }
   13984             : 
   13985             : /************************************************************************/
   13986             : /*                     GDALAttributeReadAsDouble()                      */
   13987             : /************************************************************************/
   13988             : 
   13989             : /** Return the value of an attribute as a double.
   13990             :  *
   13991             :  * This function will only return the first element if there are several.
   13992             :  *
   13993             :  * It can fail if its value can not be converted to double.
   13994             :  *
   13995             :  * This is the same as the C++ method GDALAttribute::ReadAsDouble()
   13996             :  *
   13997             :  * @return a double value.
   13998             :  */
   13999          55 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
   14000             : {
   14001          55 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   14002          55 :     return hAttr->m_poImpl->ReadAsDouble();
   14003             : }
   14004             : 
   14005             : /************************************************************************/
   14006             : /*                   GDALAttributeReadAsStringArray()                   */
   14007             : /************************************************************************/
   14008             : 
   14009             : /** Return the value of an attribute as an array of strings.
   14010             :  *
   14011             :  * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
   14012             :  *
   14013             :  * The return value must be freed with CSLDestroy().
   14014             :  */
   14015          22 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
   14016             : {
   14017          22 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   14018          22 :     return hAttr->m_poImpl->ReadAsStringArray().StealList();
   14019             : }
   14020             : 
   14021             : /************************************************************************/
   14022             : /*                    GDALAttributeReadAsIntArray()                     */
   14023             : /************************************************************************/
   14024             : 
   14025             : /** Return the value of an attribute as an array of integers.
   14026             :  *
   14027             :  * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
   14028             :  *
   14029             :  * @param hAttr Attribute
   14030             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   14031             :  * @return array to be freed with CPLFree(), or nullptr.
   14032             :  */
   14033          18 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
   14034             : {
   14035          18 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   14036          18 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   14037          18 :     *pnCount = 0;
   14038          36 :     auto tmp(hAttr->m_poImpl->ReadAsIntArray());
   14039          18 :     if (tmp.empty())
   14040           0 :         return nullptr;
   14041          18 :     auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
   14042          18 :     if (!ret)
   14043           0 :         return nullptr;
   14044          18 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
   14045          18 :     *pnCount = tmp.size();
   14046          18 :     return ret;
   14047             : }
   14048             : 
   14049             : /************************************************************************/
   14050             : /*                   GDALAttributeReadAsInt64Array()                    */
   14051             : /************************************************************************/
   14052             : 
   14053             : /** Return the value of an attribute as an array of int64_t.
   14054             :  *
   14055             :  * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
   14056             :  *
   14057             :  * @param hAttr Attribute
   14058             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   14059             :  * @return array to be freed with CPLFree(), or nullptr.
   14060             :  */
   14061          26 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
   14062             : {
   14063          26 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   14064          26 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   14065          26 :     *pnCount = 0;
   14066          52 :     auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
   14067          26 :     if (tmp.empty())
   14068           0 :         return nullptr;
   14069             :     auto ret = static_cast<int64_t *>(
   14070          26 :         VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
   14071          26 :     if (!ret)
   14072           0 :         return nullptr;
   14073          26 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
   14074          26 :     *pnCount = tmp.size();
   14075          26 :     return ret;
   14076             : }
   14077             : 
   14078             : /************************************************************************/
   14079             : /*                   GDALAttributeReadAsDoubleArray()                   */
   14080             : /************************************************************************/
   14081             : 
   14082             : /** Return the value of an attribute as an array of doubles.
   14083             :  *
   14084             :  * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
   14085             :  *
   14086             :  * @param hAttr Attribute
   14087             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   14088             :  * @return array to be freed with CPLFree(), or nullptr.
   14089             :  */
   14090          44 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
   14091             : {
   14092          44 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   14093          44 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   14094          44 :     *pnCount = 0;
   14095          88 :     auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
   14096          44 :     if (tmp.empty())
   14097           0 :         return nullptr;
   14098             :     auto ret =
   14099          44 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
   14100          44 :     if (!ret)
   14101           0 :         return nullptr;
   14102          44 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
   14103          44 :     *pnCount = tmp.size();
   14104          44 :     return ret;
   14105             : }
   14106             : 
   14107             : /************************************************************************/
   14108             : /*                       GDALAttributeWriteRaw()                        */
   14109             : /************************************************************************/
   14110             : 
   14111             : /** Write an attribute from raw values expressed in GetDataType()
   14112             :  *
   14113             :  * The values should be provided in the type of GetDataType() and there should
   14114             :  * be exactly GetTotalElementsCount() of them.
   14115             :  * If GetDataType() is a string, each value should be a char* pointer.
   14116             :  *
   14117             :  * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
   14118             :  *
   14119             :  * @param hAttr Attribute
   14120             :  * @param pabyValue Buffer of nLen bytes.
   14121             :  * @param nLength Size of pabyValue in bytes. Should be equal to
   14122             :  *             GetTotalElementsCount() * GetDataType().GetSize()
   14123             :  * @return TRUE in case of success.
   14124             :  */
   14125           5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
   14126             :                           size_t nLength)
   14127             : {
   14128           5 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   14129           5 :     return hAttr->m_poImpl->Write(pabyValue, nLength);
   14130             : }
   14131             : 
   14132             : /************************************************************************/
   14133             : /*                      GDALAttributeWriteString()                      */
   14134             : /************************************************************************/
   14135             : 
   14136             : /** Write an attribute from a string value.
   14137             :  *
   14138             :  * Type conversion will be performed if needed. If the attribute contains
   14139             :  * multiple values, only the first one will be updated.
   14140             :  *
   14141             :  * This is the same as the C++ method GDALAttribute::Write(const char*)
   14142             :  *
   14143             :  * @param hAttr Attribute
   14144             :  * @param pszVal Pointer to a string.
   14145             :  * @return TRUE in case of success.
   14146             :  */
   14147         210 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
   14148             : {
   14149         210 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   14150         210 :     return hAttr->m_poImpl->Write(pszVal);
   14151             : }
   14152             : 
   14153             : /************************************************************************/
   14154             : /*                       GDALAttributeWriteInt()                        */
   14155             : /************************************************************************/
   14156             : 
   14157             : /** Write an attribute from a integer value.
   14158             :  *
   14159             :  * Type conversion will be performed if needed. If the attribute contains
   14160             :  * multiple values, only the first one will be updated.
   14161             :  *
   14162             :  * This is the same as the C++ method GDALAttribute::WriteInt()
   14163             :  *
   14164             :  * @param hAttr Attribute
   14165             :  * @param nVal Value.
   14166             :  * @return TRUE in case of success.
   14167             :  */
   14168          23 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
   14169             : {
   14170          23 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   14171          23 :     return hAttr->m_poImpl->WriteInt(nVal);
   14172             : }
   14173             : 
   14174             : /************************************************************************/
   14175             : /*                      GDALAttributeWriteInt64()                       */
   14176             : /************************************************************************/
   14177             : 
   14178             : /** Write an attribute from an int64_t value.
   14179             :  *
   14180             :  * Type conversion will be performed if needed. If the attribute contains
   14181             :  * multiple values, only the first one will be updated.
   14182             :  *
   14183             :  * This is the same as the C++ method GDALAttribute::WriteLong()
   14184             :  *
   14185             :  * @param hAttr Attribute
   14186             :  * @param nVal Value.
   14187             :  * @return TRUE in case of success.
   14188             :  */
   14189          14 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
   14190             : {
   14191          14 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   14192          14 :     return hAttr->m_poImpl->WriteInt64(nVal);
   14193             : }
   14194             : 
   14195             : /************************************************************************/
   14196             : /*                      GDALAttributeWriteDouble()                      */
   14197             : /************************************************************************/
   14198             : 
   14199             : /** Write an attribute from a double value.
   14200             :  *
   14201             :  * Type conversion will be performed if needed. If the attribute contains
   14202             :  * multiple values, only the first one will be updated.
   14203             :  *
   14204             :  * This is the same as the C++ method GDALAttribute::Write(double);
   14205             :  *
   14206             :  * @param hAttr Attribute
   14207             :  * @param dfVal Value.
   14208             :  *
   14209             :  * @return TRUE in case of success.
   14210             :  */
   14211          12 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
   14212             : {
   14213          12 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   14214          12 :     return hAttr->m_poImpl->Write(dfVal);
   14215             : }
   14216             : 
   14217             : /************************************************************************/
   14218             : /*                   GDALAttributeWriteStringArray()                    */
   14219             : /************************************************************************/
   14220             : 
   14221             : /** Write an attribute from an array of strings.
   14222             :  *
   14223             :  * Type conversion will be performed if needed.
   14224             :  *
   14225             :  * Exactly GetTotalElementsCount() strings must be provided
   14226             :  *
   14227             :  * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
   14228             :  *
   14229             :  * @param hAttr Attribute
   14230             :  * @param papszValues Array of strings.
   14231             :  * @return TRUE in case of success.
   14232             :  */
   14233           9 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
   14234             :                                   CSLConstList papszValues)
   14235             : {
   14236           9 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   14237           9 :     return hAttr->m_poImpl->Write(papszValues);
   14238             : }
   14239             : 
   14240             : /************************************************************************/
   14241             : /*                     GDALAttributeWriteIntArray()                     */
   14242             : /************************************************************************/
   14243             : 
   14244             : /** Write an attribute from an array of int.
   14245             :  *
   14246             :  * Type conversion will be performed if needed.
   14247             :  *
   14248             :  * Exactly GetTotalElementsCount() strings must be provided
   14249             :  *
   14250             :  * This is the same as the C++ method GDALAttribute::Write(const int *,
   14251             :  * size_t)
   14252             :  *
   14253             :  * @param hAttr Attribute
   14254             :  * @param panValues Array of int.
   14255             :  * @param nCount Should be equal to GetTotalElementsCount().
   14256             :  * @return TRUE in case of success.
   14257             :  */
   14258          12 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
   14259             :                                size_t nCount)
   14260             : {
   14261          12 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   14262          12 :     return hAttr->m_poImpl->Write(panValues, nCount);
   14263             : }
   14264             : 
   14265             : /************************************************************************/
   14266             : /*                    GDALAttributeWriteInt64Array()                    */
   14267             : /************************************************************************/
   14268             : 
   14269             : /** Write an attribute from an array of int64_t.
   14270             :  *
   14271             :  * Type conversion will be performed if needed.
   14272             :  *
   14273             :  * Exactly GetTotalElementsCount() strings must be provided
   14274             :  *
   14275             :  * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
   14276             :  * size_t)
   14277             :  *
   14278             :  * @param hAttr Attribute
   14279             :  * @param panValues Array of int64_t.
   14280             :  * @param nCount Should be equal to GetTotalElementsCount().
   14281             :  * @return TRUE in case of success.
   14282             :  */
   14283          13 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
   14284             :                                  size_t nCount)
   14285             : {
   14286          13 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   14287          13 :     return hAttr->m_poImpl->Write(panValues, nCount);
   14288             : }
   14289             : 
   14290             : /************************************************************************/
   14291             : /*                   GDALAttributeWriteDoubleArray()                    */
   14292             : /************************************************************************/
   14293             : 
   14294             : /** Write an attribute from an array of double.
   14295             :  *
   14296             :  * Type conversion will be performed if needed.
   14297             :  *
   14298             :  * Exactly GetTotalElementsCount() strings must be provided
   14299             :  *
   14300             :  * This is the same as the C++ method GDALAttribute::Write(const double *,
   14301             :  * size_t)
   14302             :  *
   14303             :  * @param hAttr Attribute
   14304             :  * @param padfValues Array of double.
   14305             :  * @param nCount Should be equal to GetTotalElementsCount().
   14306             :  * @return TRUE in case of success.
   14307             :  */
   14308           8 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
   14309             :                                   const double *padfValues, size_t nCount)
   14310             : {
   14311           8 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   14312           8 :     return hAttr->m_poImpl->Write(padfValues, nCount);
   14313             : }
   14314             : 
   14315             : /************************************************************************/
   14316             : /*                        GDALAttributeRename()                         */
   14317             : /************************************************************************/
   14318             : 
   14319             : /** Rename the attribute.
   14320             :  *
   14321             :  * This is not implemented by all drivers.
   14322             :  *
   14323             :  * Drivers known to implement it: MEM, netCDF.
   14324             :  *
   14325             :  * This is the same as the C++ method GDALAbstractMDArray::Rename()
   14326             :  *
   14327             :  * @return true in case of success
   14328             :  * @since GDAL 3.8
   14329             :  */
   14330          27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
   14331             : {
   14332          27 :     VALIDATE_POINTER1(hAttr, __func__, false);
   14333          27 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   14334          27 :     return hAttr->m_poImpl->Rename(pszNewName);
   14335             : }
   14336             : 
   14337             : /************************************************************************/
   14338             : /*                        GDALDimensionRelease()                        */
   14339             : /************************************************************************/
   14340             : 
   14341             : /** Release the GDAL in-memory object associated with a GDALDimension.
   14342             :  *
   14343             :  * Note: when applied on a object coming from a driver, this does not
   14344             :  * destroy the object in the file, database, etc...
   14345             :  */
   14346        7473 : void GDALDimensionRelease(GDALDimensionH hDim)
   14347             : {
   14348        7473 :     delete hDim;
   14349        7473 : }
   14350             : 
   14351             : /************************************************************************/
   14352             : /*                        GDALDimensionGetName()                        */
   14353             : /************************************************************************/
   14354             : 
   14355             : /** Return dimension name.
   14356             :  *
   14357             :  * This is the same as the C++ method GDALDimension::GetName()
   14358             :  */
   14359         296 : const char *GDALDimensionGetName(GDALDimensionH hDim)
   14360             : {
   14361         296 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14362         296 :     return hDim->m_poImpl->GetName().c_str();
   14363             : }
   14364             : 
   14365             : /************************************************************************/
   14366             : /*                      GDALDimensionGetFullName()                      */
   14367             : /************************************************************************/
   14368             : 
   14369             : /** Return dimension full name.
   14370             :  *
   14371             :  * This is the same as the C++ method GDALDimension::GetFullName()
   14372             :  */
   14373          82 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
   14374             : {
   14375          82 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14376          82 :     return hDim->m_poImpl->GetFullName().c_str();
   14377             : }
   14378             : 
   14379             : /************************************************************************/
   14380             : /*                        GDALDimensionGetType()                        */
   14381             : /************************************************************************/
   14382             : 
   14383             : /** Return dimension type.
   14384             :  *
   14385             :  * This is the same as the C++ method GDALDimension::GetType()
   14386             :  */
   14387          70 : const char *GDALDimensionGetType(GDALDimensionH hDim)
   14388             : {
   14389          70 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14390          70 :     return hDim->m_poImpl->GetType().c_str();
   14391             : }
   14392             : 
   14393             : /************************************************************************/
   14394             : /*                     GDALDimensionGetDirection()                      */
   14395             : /************************************************************************/
   14396             : 
   14397             : /** Return dimension direction.
   14398             :  *
   14399             :  * This is the same as the C++ method GDALDimension::GetDirection()
   14400             :  */
   14401          38 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
   14402             : {
   14403          38 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14404          38 :     return hDim->m_poImpl->GetDirection().c_str();
   14405             : }
   14406             : 
   14407             : /************************************************************************/
   14408             : /*                        GDALDimensionGetSize()                        */
   14409             : /************************************************************************/
   14410             : 
   14411             : /** Return the size, that is the number of values along the dimension.
   14412             :  *
   14413             :  * This is the same as the C++ method GDALDimension::GetSize()
   14414             :  */
   14415        6028 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
   14416             : {
   14417        6028 :     VALIDATE_POINTER1(hDim, __func__, 0);
   14418        6028 :     return hDim->m_poImpl->GetSize();
   14419             : }
   14420             : 
   14421             : /************************************************************************/
   14422             : /*                  GDALDimensionGetIndexingVariable()                  */
   14423             : /************************************************************************/
   14424             : 
   14425             : /** Return the variable that is used to index the dimension (if there is one).
   14426             :  *
   14427             :  * This is the array, typically one-dimensional, describing the values taken
   14428             :  * by the dimension.
   14429             :  *
   14430             :  * The returned value should be freed with GDALMDArrayRelease().
   14431             :  *
   14432             :  * This is the same as the C++ method GDALDimension::GetIndexingVariable()
   14433             :  */
   14434         140 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
   14435             : {
   14436         140 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14437         280 :     auto var(hDim->m_poImpl->GetIndexingVariable());
   14438         140 :     if (!var)
   14439          11 :         return nullptr;
   14440         129 :     return new GDALMDArrayHS(var);
   14441             : }
   14442             : 
   14443             : /************************************************************************/
   14444             : /*                  GDALDimensionSetIndexingVariable()                  */
   14445             : /************************************************************************/
   14446             : 
   14447             : /** Set the variable that is used to index the dimension.
   14448             :  *
   14449             :  * This is the array, typically one-dimensional, describing the values taken
   14450             :  * by the dimension.
   14451             :  *
   14452             :  * This is the same as the C++ method GDALDimension::SetIndexingVariable()
   14453             :  *
   14454             :  * @return TRUE in case of success.
   14455             :  */
   14456          27 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
   14457             : {
   14458          27 :     VALIDATE_POINTER1(hDim, __func__, FALSE);
   14459          81 :     return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
   14460          54 :                                                       : nullptr);
   14461             : }
   14462             : 
   14463             : /************************************************************************/
   14464             : /*                        GDALDimensionRename()                         */
   14465             : /************************************************************************/
   14466             : 
   14467             : /** Rename the dimension.
   14468             :  *
   14469             :  * This is not implemented by all drivers.
   14470             :  *
   14471             :  * Drivers known to implement it: MEM, netCDF.
   14472             :  *
   14473             :  * This is the same as the C++ method GDALDimension::Rename()
   14474             :  *
   14475             :  * @return true in case of success
   14476             :  * @since GDAL 3.8
   14477             :  */
   14478          31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
   14479             : {
   14480          31 :     VALIDATE_POINTER1(hDim, __func__, false);
   14481          31 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   14482          31 :     return hDim->m_poImpl->Rename(pszNewName);
   14483             : }
   14484             : 
   14485             : /************************************************************************/
   14486             : /*                      GDALDatasetGetRootGroup()                       */
   14487             : /************************************************************************/
   14488             : 
   14489             : /** Return the root GDALGroup of this dataset.
   14490             :  *
   14491             :  * Only valid for multidimensional datasets.
   14492             :  *
   14493             :  * The returned value must be freed with GDALGroupRelease().
   14494             :  *
   14495             :  * This is the same as the C++ method GDALDataset::GetRootGroup().
   14496             :  *
   14497             :  * @since GDAL 3.1
   14498             :  */
   14499        2093 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
   14500             : {
   14501        2093 :     VALIDATE_POINTER1(hDS, __func__, nullptr);
   14502        2093 :     auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
   14503        2093 :     return poGroup ? new GDALGroupHS(poGroup) : nullptr;
   14504             : }
   14505             : 
   14506             : /************************************************************************/
   14507             : /*                      GDALRasterBandAsMDArray()                       */
   14508             : /************************************************************************/
   14509             : 
   14510             : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
   14511             :  *
   14512             :  * The band must be linked to a GDALDataset. If this dataset is not already
   14513             :  * marked as shared, it will be, so that the returned array holds a reference
   14514             :  * to it.
   14515             :  *
   14516             :  * If the dataset has a geotransform attached, the X and Y dimensions of the
   14517             :  * returned array will have an associated indexing variable.
   14518             :  *
   14519             :  * The returned pointer must be released with GDALMDArrayRelease().
   14520             :  *
   14521             :  * This is the same as the C++ method GDALRasterBand::AsMDArray().
   14522             :  *
   14523             :  * @return a new array, or NULL.
   14524             :  *
   14525             :  * @since GDAL 3.1
   14526             :  */
   14527          25 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
   14528             : {
   14529          25 :     VALIDATE_POINTER1(hBand, __func__, nullptr);
   14530          50 :     auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
   14531          25 :     if (!poArray)
   14532           0 :         return nullptr;
   14533          25 :     return new GDALMDArrayHS(poArray);
   14534             : }
   14535             : 
   14536             : /************************************************************************/
   14537             : /*                        GDALDatasetAsMDArray()                        */
   14538             : /************************************************************************/
   14539             : 
   14540             : /** Return a view of this dataset as a 3D multidimensional GDALMDArray.
   14541             :  *
   14542             :  * If this dataset is not already marked as shared, it will be, so that the
   14543             :  * returned array holds a reference to it.
   14544             :  *
   14545             :  * If the dataset has a geotransform attached, the X and Y dimensions of the
   14546             :  * returned array will have an associated indexing variable.
   14547             :  *
   14548             :  * The currently supported list of options is:
   14549             :  * <ul>
   14550             :  * <li>DIM_ORDER=&lt;order&gt; where order can be "AUTO", "Band,Y,X" or "Y,X,Band".
   14551             :  * "Band,Y,X" means that the first (slowest changing) dimension is Band
   14552             :  * and the last (fastest changing direction) is X
   14553             :  * "Y,X,Band" means that the first (slowest changing) dimension is Y
   14554             :  * and the last (fastest changing direction) is Band.
   14555             :  * "AUTO" (the default) selects "Band,Y,X" for single band datasets, or takes
   14556             :  * into account the INTERLEAVE metadata item in the IMAGE_STRUCTURE domain.
   14557             :  * If it equals BAND, then "Band,Y,X" is used. Otherwise (if it equals PIXEL),
   14558             :  * "Y,X,Band" is use.
   14559             :  * </li>
   14560             :  * <li>BAND_INDEXING_VAR_ITEM={Description}|{None}|{Index}|{ColorInterpretation}|&lt;BandMetadataItem&gt;:
   14561             :  * item from which to build the band indexing variable.
   14562             :  * <ul>
   14563             :  * <li>"{Description}", the default, means to use the band description (or "Band index" if empty).</li>
   14564             :  * <li>"{None}" means that no band indexing variable must be created.</li>
   14565             :  * <li>"{Index}" means that the band index (starting at one) is used.</li>
   14566             :  * <li>"{ColorInterpretation}" means that the band color interpretation is used (i.e. "Red", "Green", "Blue").</li>
   14567             :  * <li>&lt;BandMetadataItem&gt; is the name of a band metadata item to use.</li>
   14568             :  * </ul>
   14569             :  * </li>
   14570             :  * <li>BAND_INDEXING_VAR_TYPE=String|Real|Integer: the data type of the band
   14571             :  * indexing variable, when BAND_INDEXING_VAR_ITEM corresponds to a band metadata item.
   14572             :  * Defaults to String.
   14573             :  * </li>
   14574             :  * <li>BAND_DIM_NAME=&lt;string&gt;: Name of the band dimension.
   14575             :  * Defaults to "Band".
   14576             :  * </li>
   14577             :  * <li>X_DIM_NAME=&lt;string&gt;: Name of the X dimension. Defaults to "X".
   14578             :  * </li>
   14579             :  * <li>Y_DIM_NAME=&lt;string&gt;: Name of the Y dimension. Defaults to "Y".
   14580             :  * </li>
   14581             :  * </ul>
   14582             :  *
   14583             :  * The returned pointer must be released with GDALMDArrayRelease().
   14584             :  *
   14585             :  * The "reverse" methods are GDALRasterBand::AsMDArray() and
   14586             :  * GDALDataset::AsMDArray()
   14587             :  *
   14588             :  * This is the same as the C++ method GDALDataset::AsMDArray().
   14589             :  *
   14590             :  * @param hDS Dataset handle.
   14591             :  * @param papszOptions Null-terminated list of strings, or nullptr.
   14592             :  * @return a new array, or NULL.
   14593             :  *
   14594             :  * @since GDAL 3.12
   14595             :  */
   14596          15 : GDALMDArrayH GDALDatasetAsMDArray(GDALDatasetH hDS, CSLConstList papszOptions)
   14597             : {
   14598          15 :     VALIDATE_POINTER1(hDS, __func__, nullptr);
   14599          30 :     auto poArray(GDALDataset::FromHandle(hDS)->AsMDArray(papszOptions));
   14600          15 :     if (!poArray)
   14601           3 :         return nullptr;
   14602          12 :     return new GDALMDArrayHS(poArray);
   14603             : }
   14604             : 
   14605             : /************************************************************************/
   14606             : /*                    GDALMDArrayAsClassicDataset()                     */
   14607             : /************************************************************************/
   14608             : 
   14609             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   14610             :  *
   14611             :  * Only 2D or more arrays are supported.
   14612             :  *
   14613             :  * In the case of > 2D arrays, additional dimensions will be represented as
   14614             :  * raster bands.
   14615             :  *
   14616             :  * The "reverse" methods are GDALRasterBand::AsMDArray() and
   14617             :  * GDALDataset::AsMDArray()
   14618             :  *
   14619             :  * This is the same as the C++ method GDALMDArray::AsClassicDataset().
   14620             :  *
   14621             :  * @param hArray Array.
   14622             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   14623             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   14624             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   14625             :  */
   14626           0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
   14627             :                                          size_t iYDim)
   14628             : {
   14629           0 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   14630           0 :     return GDALDataset::ToHandle(
   14631           0 :         hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
   14632             : }
   14633             : 
   14634             : /************************************************************************/
   14635             : /*                   GDALMDArrayAsClassicDatasetEx()                    */
   14636             : /************************************************************************/
   14637             : 
   14638             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   14639             :  *
   14640             :  * Only 2D or more arrays are supported.
   14641             :  *
   14642             :  * In the case of > 2D arrays, additional dimensions will be represented as
   14643             :  * raster bands.
   14644             :  *
   14645             :  * The "reverse" method is GDALRasterBand::AsMDArray().
   14646             :  *
   14647             :  * This is the same as the C++ method GDALMDArray::AsClassicDataset().
   14648             :  * @param hArray Array.
   14649             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   14650             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   14651             :  *              Ignored if the dimension count is 1.
   14652             :  * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
   14653             :  *                   BAND_IMAGERY_METADATA option.
   14654             :  * @param papszOptions Cf GDALMDArray::AsClassicDataset()
   14655             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   14656             :  * @since GDAL 3.8
   14657             :  */
   14658         110 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
   14659             :                                            size_t iYDim, GDALGroupH hRootGroup,
   14660             :                                            CSLConstList papszOptions)
   14661             : {
   14662         110 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   14663         220 :     return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
   14664         220 :         iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
   14665         220 :         papszOptions));
   14666             : }
   14667             : 
   14668             : //! @cond Doxygen_Suppress
   14669             : 
   14670         180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
   14671             :                                          const std::string &osName,
   14672             :                                          const std::string &osValue,
   14673         180 :                                          GDALExtendedDataTypeSubType eSubType)
   14674             :     : GDALAbstractMDArray(osParentName, osName),
   14675             :       GDALAttribute(osParentName, osName),
   14676         180 :       m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
   14677             : {
   14678         180 : }
   14679             : 
   14680             : const std::vector<std::shared_ptr<GDALDimension>> &
   14681          30 : GDALAttributeString::GetDimensions() const
   14682             : {
   14683          30 :     return m_dims;
   14684             : }
   14685             : 
   14686          21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
   14687             : {
   14688          21 :     return m_dt;
   14689             : }
   14690             : 
   14691          10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
   14692             :                                 const GPtrDiff_t *,
   14693             :                                 const GDALExtendedDataType &bufferDataType,
   14694             :                                 void *pDstBuffer) const
   14695             : {
   14696          10 :     if (bufferDataType.GetClass() != GEDTC_STRING)
   14697           0 :         return false;
   14698          10 :     char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
   14699          10 :     if (!pszStr)
   14700           0 :         return false;
   14701          10 :     memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
   14702          10 :     *static_cast<char **>(pDstBuffer) = pszStr;
   14703          10 :     return true;
   14704             : }
   14705             : 
   14706          66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   14707             :                                            const std::string &osName,
   14708          66 :                                            double dfValue)
   14709             :     : GDALAbstractMDArray(osParentName, osName),
   14710             :       GDALAttribute(osParentName, osName),
   14711          66 :       m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
   14712             : {
   14713          66 : }
   14714             : 
   14715          27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   14716             :                                            const std::string &osName,
   14717          27 :                                            int nValue)
   14718             :     : GDALAbstractMDArray(osParentName, osName),
   14719             :       GDALAttribute(osParentName, osName),
   14720          27 :       m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
   14721             : {
   14722          27 : }
   14723             : 
   14724           7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   14725             :                                            const std::string &osName,
   14726           7 :                                            const std::vector<GUInt32> &anValues)
   14727             :     : GDALAbstractMDArray(osParentName, osName),
   14728             :       GDALAttribute(osParentName, osName),
   14729           7 :       m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
   14730             : {
   14731           7 :     m_dims.push_back(std::make_shared<GDALDimension>(
   14732          14 :         std::string(), "dim0", std::string(), std::string(),
   14733           7 :         m_anValuesUInt32.size()));
   14734           7 : }
   14735             : 
   14736             : const std::vector<std::shared_ptr<GDALDimension>> &
   14737          14 : GDALAttributeNumeric::GetDimensions() const
   14738             : {
   14739          14 :     return m_dims;
   14740             : }
   14741             : 
   14742           8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
   14743             : {
   14744           8 :     return m_dt;
   14745             : }
   14746             : 
   14747           4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
   14748             :                                  const size_t *count, const GInt64 *arrayStep,
   14749             :                                  const GPtrDiff_t *bufferStride,
   14750             :                                  const GDALExtendedDataType &bufferDataType,
   14751             :                                  void *pDstBuffer) const
   14752             : {
   14753           4 :     if (m_dims.empty())
   14754             :     {
   14755           3 :         if (m_dt.GetNumericDataType() == GDT_Float64)
   14756           0 :             GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
   14757             :                                             bufferDataType);
   14758             :         else
   14759             :         {
   14760           3 :             CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
   14761           3 :             GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
   14762             :                                             bufferDataType);
   14763             :         }
   14764             :     }
   14765             :     else
   14766             :     {
   14767           1 :         CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
   14768           1 :         GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
   14769          30 :         for (size_t i = 0; i < count[0]; ++i)
   14770             :         {
   14771          29 :             GDALExtendedDataType::CopyValue(
   14772          29 :                 &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
   14773          29 :                                                       i * arrayStep[0])],
   14774          29 :                 m_dt, pabyDstBuffer, bufferDataType);
   14775          29 :             pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
   14776             :         }
   14777             :     }
   14778           4 :     return true;
   14779             : }
   14780             : 
   14781         380 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
   14782             :     const std::string &osParentName, const std::string &osName,
   14783             :     const std::shared_ptr<GDALDimension> &poDim, double dfStart,
   14784         380 :     double dfIncrement, double dfOffsetInIncrement)
   14785             :     : GDALAbstractMDArray(osParentName, osName),
   14786             :       GDALMDArray(osParentName, osName), m_dfStart(dfStart),
   14787             :       m_dfIncrement(dfIncrement), m_dfOffsetInIncrement(dfOffsetInIncrement),
   14788         760 :       m_dims{poDim}
   14789             : {
   14790         380 : }
   14791             : 
   14792         380 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
   14793             :     const std::string &osParentName, const std::string &osName,
   14794             :     const std::shared_ptr<GDALDimension> &poDim, double dfStart,
   14795             :     double dfIncrement, double dfOffsetInIncrement)
   14796             : {
   14797             :     auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
   14798         380 :         osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
   14799         380 :     poArray->SetSelf(poArray);
   14800         380 :     return poArray;
   14801             : }
   14802             : 
   14803             : const std::vector<std::shared_ptr<GDALDimension>> &
   14804        1884 : GDALMDArrayRegularlySpaced::GetDimensions() const
   14805             : {
   14806        1884 :     return m_dims;
   14807             : }
   14808             : 
   14809         679 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
   14810             : {
   14811         679 :     return m_dt;
   14812             : }
   14813             : 
   14814             : std::vector<std::shared_ptr<GDALAttribute>>
   14815           4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
   14816             : {
   14817           4 :     return m_attributes;
   14818             : }
   14819             : 
   14820           0 : void GDALMDArrayRegularlySpaced::AddAttribute(
   14821             :     const std::shared_ptr<GDALAttribute> &poAttr)
   14822             : {
   14823           0 :     m_attributes.emplace_back(poAttr);
   14824           0 : }
   14825             : 
   14826         248 : bool GDALMDArrayRegularlySpaced::IRead(
   14827             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
   14828             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
   14829             :     void *pDstBuffer) const
   14830             : {
   14831         248 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
   14832        4690 :     for (size_t i = 0; i < count[0]; i++)
   14833             :     {
   14834        4442 :         const double dfVal =
   14835        4442 :             m_dfStart +
   14836        4442 :             (arrayStartIdx[0] + i * static_cast<double>(arrayStep[0]) +
   14837        4442 :              m_dfOffsetInIncrement) *
   14838        4442 :                 m_dfIncrement;
   14839        4442 :         GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
   14840             :                                         bufferDataType);
   14841        4442 :         pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
   14842             :     }
   14843         248 :     return true;
   14844             : }
   14845             : 
   14846         268 : bool GDALMDArrayRegularlySpaced::IsRegularlySpaced(double &dfStart,
   14847             :                                                    double &dfIncrement) const
   14848             : {
   14849         268 :     dfStart = m_dfStart + m_dfOffsetInIncrement * m_dfIncrement;
   14850         268 :     dfIncrement = m_dfIncrement;
   14851         268 :     return true;
   14852             : }
   14853             : 
   14854        6888 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
   14855             :     const std::string &osParentName, const std::string &osName,
   14856        6888 :     const std::string &osType, const std::string &osDirection, GUInt64 nSize)
   14857        6888 :     : GDALDimension(osParentName, osName, osType, osDirection, nSize)
   14858             : {
   14859        6888 : }
   14860             : 
   14861             : std::shared_ptr<GDALMDArray>
   14862        2278 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
   14863             : {
   14864        2278 :     return m_poIndexingVariable.lock();
   14865             : }
   14866             : 
   14867             : // cppcheck-suppress passedByValue
   14868         974 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
   14869             :     std::shared_ptr<GDALMDArray> poIndexingVariable)
   14870             : {
   14871         974 :     m_poIndexingVariable = poIndexingVariable;
   14872         974 :     return true;
   14873             : }
   14874             : 
   14875          33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
   14876             : {
   14877          33 :     m_nSize = nNewSize;
   14878          33 : }
   14879             : 
   14880             : /************************************************************************/
   14881             : /*                       GDALPamMultiDim::Private                       */
   14882             : /************************************************************************/
   14883             : 
   14884             : struct GDALPamMultiDim::Private
   14885             : {
   14886             :     std::string m_osFilename{};
   14887             :     std::string m_osPamFilename{};
   14888             : 
   14889             :     struct Statistics
   14890             :     {
   14891             :         bool bHasStats = false;
   14892             :         bool bApproxStats = false;
   14893             :         double dfMin = 0;
   14894             :         double dfMax = 0;
   14895             :         double dfMean = 0;
   14896             :         double dfStdDev = 0;
   14897             :         GUInt64 nValidCount = 0;
   14898             :     };
   14899             : 
   14900             :     struct ArrayInfo
   14901             :     {
   14902             :         std::shared_ptr<OGRSpatialReference> poSRS{};
   14903             :         // cppcheck-suppress unusedStructMember
   14904             :         Statistics stats{};
   14905             :     };
   14906             : 
   14907             :     typedef std::pair<std::string, std::string> NameContext;
   14908             :     std::map<NameContext, ArrayInfo> m_oMapArray{};
   14909             :     std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
   14910             :     bool m_bDirty = false;
   14911             :     bool m_bLoaded = false;
   14912             : };
   14913             : 
   14914             : /************************************************************************/
   14915             : /*                           GDALPamMultiDim                            */
   14916             : /************************************************************************/
   14917             : 
   14918        2720 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
   14919        2720 :     : d(new Private())
   14920             : {
   14921        2720 :     d->m_osFilename = osFilename;
   14922        2720 : }
   14923             : 
   14924             : /************************************************************************/
   14925             : /*                 GDALPamMultiDim::~GDALPamMultiDim()                  */
   14926             : /************************************************************************/
   14927             : 
   14928        2720 : GDALPamMultiDim::~GDALPamMultiDim()
   14929             : {
   14930        2720 :     if (d->m_bDirty)
   14931          35 :         Save();
   14932        2720 : }
   14933             : 
   14934             : /************************************************************************/
   14935             : /*                       GDALPamMultiDim::Load()                        */
   14936             : /************************************************************************/
   14937             : 
   14938         131 : void GDALPamMultiDim::Load()
   14939             : {
   14940         131 :     if (d->m_bLoaded)
   14941         120 :         return;
   14942          52 :     d->m_bLoaded = true;
   14943             : 
   14944          52 :     const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
   14945          52 :     d->m_osPamFilename =
   14946         104 :         pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
   14947          52 :     CPLXMLTreeCloser oTree(nullptr);
   14948             :     {
   14949         104 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
   14950          52 :         oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
   14951             :     }
   14952          52 :     if (!oTree)
   14953             :     {
   14954          41 :         return;
   14955             :     }
   14956          11 :     const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
   14957          11 :     if (!poPAMMultiDim)
   14958           0 :         return;
   14959          35 :     for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
   14960          24 :          psIter = psIter->psNext)
   14961             :     {
   14962          24 :         if (psIter->eType == CXT_Element &&
   14963          24 :             strcmp(psIter->pszValue, "Array") == 0)
   14964             :         {
   14965          13 :             const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
   14966          13 :             if (!pszName)
   14967           0 :                 continue;
   14968          13 :             const char *pszContext = CPLGetXMLValue(psIter, "context", "");
   14969             :             const auto oKey =
   14970          26 :                 std::pair<std::string, std::string>(pszName, pszContext);
   14971             : 
   14972             :             /* --------------------------------------------------------------------
   14973             :              */
   14974             :             /*      Check for an SRS node. */
   14975             :             /* --------------------------------------------------------------------
   14976             :              */
   14977          13 :             const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
   14978          13 :             if (psSRSNode)
   14979             :             {
   14980             :                 std::shared_ptr<OGRSpatialReference> poSRS =
   14981           6 :                     std::make_shared<OGRSpatialReference>();
   14982           3 :                 poSRS->SetFromUserInput(
   14983             :                     CPLGetXMLValue(psSRSNode, nullptr, ""),
   14984             :                     OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
   14985           3 :                 const char *pszMapping = CPLGetXMLValue(
   14986             :                     psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
   14987           3 :                 if (pszMapping)
   14988             :                 {
   14989             :                     char **papszTokens =
   14990           3 :                         CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
   14991           6 :                     std::vector<int> anMapping;
   14992           9 :                     for (int i = 0; papszTokens && papszTokens[i]; i++)
   14993             :                     {
   14994           6 :                         anMapping.push_back(atoi(papszTokens[i]));
   14995             :                     }
   14996           3 :                     CSLDestroy(papszTokens);
   14997           3 :                     poSRS->SetDataAxisToSRSAxisMapping(anMapping);
   14998             :                 }
   14999             :                 else
   15000             :                 {
   15001           0 :                     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
   15002             :                 }
   15003             : 
   15004             :                 const char *pszCoordinateEpoch =
   15005           3 :                     CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
   15006           3 :                 if (pszCoordinateEpoch)
   15007           3 :                     poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
   15008             : 
   15009           3 :                 d->m_oMapArray[oKey].poSRS = std::move(poSRS);
   15010             :             }
   15011             : 
   15012             :             const CPLXMLNode *psStatistics =
   15013          13 :                 CPLGetXMLNode(psIter, "Statistics");
   15014          13 :             if (psStatistics)
   15015             :             {
   15016           7 :                 Private::Statistics sStats;
   15017           7 :                 sStats.bHasStats = true;
   15018           7 :                 sStats.bApproxStats = CPLTestBool(
   15019             :                     CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
   15020           7 :                 sStats.dfMin =
   15021           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
   15022           7 :                 sStats.dfMax =
   15023           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
   15024           7 :                 sStats.dfMean =
   15025           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
   15026           7 :                 sStats.dfStdDev =
   15027           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
   15028           7 :                 sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
   15029             :                     CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
   15030           7 :                 d->m_oMapArray[oKey].stats = sStats;
   15031          13 :             }
   15032             :         }
   15033             :         else
   15034             :         {
   15035          11 :             CPLXMLNode *psNextBackup = psIter->psNext;
   15036          11 :             psIter->psNext = nullptr;
   15037          11 :             d->m_apoOtherNodes.emplace_back(
   15038          11 :                 CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
   15039          11 :             psIter->psNext = psNextBackup;
   15040             :         }
   15041             :     }
   15042             : }
   15043             : 
   15044             : /************************************************************************/
   15045             : /*                       GDALPamMultiDim::Save()                        */
   15046             : /************************************************************************/
   15047             : 
   15048          35 : void GDALPamMultiDim::Save()
   15049             : {
   15050             :     CPLXMLTreeCloser oTree(
   15051          70 :         CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
   15052          39 :     for (const auto &poOtherNode : d->m_apoOtherNodes)
   15053             :     {
   15054           4 :         CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
   15055             :     }
   15056         132 :     for (const auto &kv : d->m_oMapArray)
   15057             :     {
   15058             :         CPLXMLNode *psArrayNode =
   15059          97 :             CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
   15060          97 :         CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
   15061          97 :         if (!kv.first.second.empty())
   15062             :         {
   15063           1 :             CPLAddXMLAttributeAndValue(psArrayNode, "context",
   15064             :                                        kv.first.second.c_str());
   15065             :         }
   15066          97 :         if (kv.second.poSRS)
   15067             :         {
   15068          86 :             char *pszWKT = nullptr;
   15069             :             {
   15070         172 :                 CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
   15071          86 :                 const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
   15072          86 :                 kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
   15073             :             }
   15074             :             CPLXMLNode *psSRSNode =
   15075          86 :                 CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
   15076          86 :             CPLFree(pszWKT);
   15077             :             const auto &mapping =
   15078          86 :                 kv.second.poSRS->GetDataAxisToSRSAxisMapping();
   15079         172 :             CPLString osMapping;
   15080         258 :             for (size_t i = 0; i < mapping.size(); ++i)
   15081             :             {
   15082         172 :                 if (!osMapping.empty())
   15083          86 :                     osMapping += ",";
   15084         172 :                 osMapping += CPLSPrintf("%d", mapping[i]);
   15085             :             }
   15086          86 :             CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
   15087             :                                        osMapping.c_str());
   15088             : 
   15089             :             const double dfCoordinateEpoch =
   15090          86 :                 kv.second.poSRS->GetCoordinateEpoch();
   15091          86 :             if (dfCoordinateEpoch > 0)
   15092             :             {
   15093             :                 std::string osCoordinateEpoch =
   15094           2 :                     CPLSPrintf("%f", dfCoordinateEpoch);
   15095           1 :                 if (osCoordinateEpoch.find('.') != std::string::npos)
   15096             :                 {
   15097           6 :                     while (osCoordinateEpoch.back() == '0')
   15098           5 :                         osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
   15099             :                 }
   15100           1 :                 CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
   15101             :                                            osCoordinateEpoch.c_str());
   15102             :             }
   15103             :         }
   15104             : 
   15105          97 :         if (kv.second.stats.bHasStats)
   15106             :         {
   15107             :             CPLXMLNode *psMDArray =
   15108           8 :                 CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
   15109           8 :             CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
   15110           8 :                                         kv.second.stats.bApproxStats ? "1"
   15111             :                                                                      : "0");
   15112           8 :             CPLCreateXMLElementAndValue(
   15113             :                 psMDArray, "Minimum",
   15114           8 :                 CPLSPrintf("%.17g", kv.second.stats.dfMin));
   15115           8 :             CPLCreateXMLElementAndValue(
   15116             :                 psMDArray, "Maximum",
   15117           8 :                 CPLSPrintf("%.17g", kv.second.stats.dfMax));
   15118           8 :             CPLCreateXMLElementAndValue(
   15119           8 :                 psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
   15120           8 :             CPLCreateXMLElementAndValue(
   15121             :                 psMDArray, "StdDev",
   15122           8 :                 CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
   15123           8 :             CPLCreateXMLElementAndValue(
   15124             :                 psMDArray, "ValidSampleCount",
   15125           8 :                 CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
   15126             :         }
   15127             :     }
   15128             : 
   15129             :     int bSaved;
   15130          70 :     CPLErrorAccumulator oErrorAccumulator;
   15131             :     {
   15132          35 :         auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
   15133          35 :         CPL_IGNORE_RET_VAL(oAccumulator);
   15134             :         bSaved =
   15135          35 :             CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
   15136             :     }
   15137             : 
   15138          35 :     const char *pszNewPam = nullptr;
   15139          35 :     if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
   15140           0 :         ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
   15141             :     {
   15142           0 :         CPLErrorReset();
   15143           0 :         CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
   15144             :     }
   15145             :     else
   15146             :     {
   15147          35 :         oErrorAccumulator.ReplayErrors();
   15148             :     }
   15149          35 : }
   15150             : 
   15151             : /************************************************************************/
   15152             : /*                   GDALPamMultiDim::GetSpatialRef()                   */
   15153             : /************************************************************************/
   15154             : 
   15155             : std::shared_ptr<OGRSpatialReference>
   15156          19 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
   15157             :                                const std::string &osContext)
   15158             : {
   15159          19 :     Load();
   15160             :     auto oIter =
   15161          19 :         d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
   15162          19 :     if (oIter != d->m_oMapArray.end())
   15163           2 :         return oIter->second.poSRS;
   15164          17 :     return nullptr;
   15165             : }
   15166             : 
   15167             : /************************************************************************/
   15168             : /*                   GDALPamMultiDim::SetSpatialRef()                   */
   15169             : /************************************************************************/
   15170             : 
   15171          87 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
   15172             :                                     const std::string &osContext,
   15173             :                                     const OGRSpatialReference *poSRS)
   15174             : {
   15175          87 :     Load();
   15176          87 :     d->m_bDirty = true;
   15177          87 :     if (poSRS && !poSRS->IsEmpty())
   15178          86 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
   15179             :             poSRS->Clone());
   15180             :     else
   15181           2 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
   15182           1 :             .poSRS.reset();
   15183          87 : }
   15184             : 
   15185             : /************************************************************************/
   15186             : /*                           GetStatistics()                            */
   15187             : /************************************************************************/
   15188             : 
   15189          16 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
   15190             :                                       const std::string &osContext,
   15191             :                                       bool bApproxOK, double *pdfMin,
   15192             :                                       double *pdfMax, double *pdfMean,
   15193             :                                       double *pdfStdDev, GUInt64 *pnValidCount)
   15194             : {
   15195          16 :     Load();
   15196             :     auto oIter =
   15197          16 :         d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
   15198          16 :     if (oIter == d->m_oMapArray.end())
   15199           9 :         return CE_Failure;
   15200           7 :     const auto &stats = oIter->second.stats;
   15201           7 :     if (!stats.bHasStats)
   15202           1 :         return CE_Failure;
   15203           6 :     if (!bApproxOK && stats.bApproxStats)
   15204           0 :         return CE_Failure;
   15205           6 :     if (pdfMin)
   15206           6 :         *pdfMin = stats.dfMin;
   15207           6 :     if (pdfMax)
   15208           6 :         *pdfMax = stats.dfMax;
   15209           6 :     if (pdfMean)
   15210           6 :         *pdfMean = stats.dfMean;
   15211           6 :     if (pdfStdDev)
   15212           6 :         *pdfStdDev = stats.dfStdDev;
   15213           6 :     if (pnValidCount)
   15214           6 :         *pnValidCount = stats.nValidCount;
   15215           6 :     return CE_None;
   15216             : }
   15217             : 
   15218             : /************************************************************************/
   15219             : /*                           SetStatistics()                            */
   15220             : /************************************************************************/
   15221             : 
   15222           8 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
   15223             :                                     const std::string &osContext,
   15224             :                                     bool bApproxStats, double dfMin,
   15225             :                                     double dfMax, double dfMean,
   15226             :                                     double dfStdDev, GUInt64 nValidCount)
   15227             : {
   15228           8 :     Load();
   15229           8 :     d->m_bDirty = true;
   15230             :     auto &stats =
   15231           8 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
   15232           8 :     stats.bHasStats = true;
   15233           8 :     stats.bApproxStats = bApproxStats;
   15234           8 :     stats.dfMin = dfMin;
   15235           8 :     stats.dfMax = dfMax;
   15236           8 :     stats.dfMean = dfMean;
   15237           8 :     stats.dfStdDev = dfStdDev;
   15238           8 :     stats.nValidCount = nValidCount;
   15239           8 : }
   15240             : 
   15241             : /************************************************************************/
   15242             : /*                          ClearStatistics()                           */
   15243             : /************************************************************************/
   15244             : 
   15245           0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
   15246             :                                       const std::string &osContext)
   15247             : {
   15248           0 :     Load();
   15249           0 :     d->m_bDirty = true;
   15250           0 :     d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
   15251             :         false;
   15252           0 : }
   15253             : 
   15254             : /************************************************************************/
   15255             : /*                          ClearStatistics()                           */
   15256             : /************************************************************************/
   15257             : 
   15258           1 : void GDALPamMultiDim::ClearStatistics()
   15259             : {
   15260           1 :     Load();
   15261           1 :     d->m_bDirty = true;
   15262           3 :     for (auto &kv : d->m_oMapArray)
   15263           2 :         kv.second.stats.bHasStats = false;
   15264           1 : }
   15265             : 
   15266             : /************************************************************************/
   15267             : /*                               GetPAM()                               */
   15268             : /************************************************************************/
   15269             : 
   15270             : /*static*/ std::shared_ptr<GDALPamMultiDim>
   15271        1478 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
   15272             : {
   15273        1478 :     auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
   15274        1478 :     if (poPamArray)
   15275         745 :         return poPamArray->GetPAM();
   15276         733 :     return nullptr;
   15277             : }
   15278             : 
   15279             : /************************************************************************/
   15280             : /*                            GDALPamMDArray                            */
   15281             : /************************************************************************/
   15282             : 
   15283        6199 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
   15284             :                                const std::string &osName,
   15285             :                                const std::shared_ptr<GDALPamMultiDim> &poPam,
   15286           0 :                                const std::string &osContext)
   15287             :     :
   15288             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
   15289             :       GDALAbstractMDArray(osParentName, osName),
   15290             : #endif
   15291        6199 :       GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
   15292             : {
   15293        6199 : }
   15294             : 
   15295             : /************************************************************************/
   15296             : /*                   GDALPamMDArray::SetSpatialRef()                    */
   15297             : /************************************************************************/
   15298             : 
   15299          87 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
   15300             : {
   15301          87 :     if (!m_poPam)
   15302           0 :         return false;
   15303          87 :     m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
   15304          87 :     return true;
   15305             : }
   15306             : 
   15307             : /************************************************************************/
   15308             : /*                   GDALPamMDArray::GetSpatialRef()                    */
   15309             : /************************************************************************/
   15310             : 
   15311          19 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
   15312             : {
   15313          19 :     if (!m_poPam)
   15314           0 :         return nullptr;
   15315          19 :     return m_poPam->GetSpatialRef(GetFullName(), GetContext());
   15316             : }
   15317             : 
   15318             : /************************************************************************/
   15319             : /*                           GetStatistics()                            */
   15320             : /************************************************************************/
   15321             : 
   15322          16 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
   15323             :                                      double *pdfMin, double *pdfMax,
   15324             :                                      double *pdfMean, double *pdfStdDev,
   15325             :                                      GUInt64 *pnValidCount,
   15326             :                                      GDALProgressFunc pfnProgress,
   15327             :                                      void *pProgressData)
   15328             : {
   15329          16 :     if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
   15330             :                                           bApproxOK, pdfMin, pdfMax, pdfMean,
   15331          16 :                                           pdfStdDev, pnValidCount) == CE_None)
   15332             :     {
   15333           6 :         return CE_None;
   15334             :     }
   15335          10 :     if (!bForce)
   15336           4 :         return CE_Warning;
   15337             : 
   15338           6 :     return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
   15339             :                                       pdfMean, pdfStdDev, pnValidCount,
   15340           6 :                                       pfnProgress, pProgressData);
   15341             : }
   15342             : 
   15343             : /************************************************************************/
   15344             : /*                           SetStatistics()                            */
   15345             : /************************************************************************/
   15346             : 
   15347           8 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
   15348             :                                    double dfMax, double dfMean, double dfStdDev,
   15349             :                                    GUInt64 nValidCount,
   15350             :                                    CSLConstList /* papszOptions */)
   15351             : {
   15352           8 :     if (!m_poPam)
   15353           0 :         return false;
   15354           8 :     m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
   15355             :                            dfMax, dfMean, dfStdDev, nValidCount);
   15356           8 :     return true;
   15357             : }
   15358             : 
   15359             : /************************************************************************/
   15360             : /*                          ClearStatistics()                           */
   15361             : /************************************************************************/
   15362             : 
   15363           0 : void GDALPamMDArray::ClearStatistics()
   15364             : {
   15365           0 :     if (!m_poPam)
   15366           0 :         return;
   15367           0 :     m_poPam->ClearStatistics(GetFullName(), GetContext());
   15368             : }
   15369             : 
   15370             : /************************************************************************/
   15371             : /*                 GDALMDIAsAttribute::GetDimensions()                  */
   15372             : /************************************************************************/
   15373             : 
   15374             : const std::vector<std::shared_ptr<GDALDimension>> &
   15375          29 : GDALMDIAsAttribute::GetDimensions() const
   15376             : {
   15377          29 :     return m_dims;
   15378             : }
   15379             : 
   15380             : /************************************************************************/
   15381             : /*         GDALMDArrayRawBlockInfo::~GDALMDArrayRawBlockInfo()          */
   15382             : /************************************************************************/
   15383             : 
   15384          64 : GDALMDArrayRawBlockInfo::~GDALMDArrayRawBlockInfo()
   15385             : {
   15386          32 :     clear();
   15387          32 : }
   15388             : 
   15389             : /************************************************************************/
   15390             : /*                   GDALMDArrayRawBlockInfo::clear()                   */
   15391             : /************************************************************************/
   15392             : 
   15393          53 : void GDALMDArrayRawBlockInfo::clear()
   15394             : {
   15395          53 :     CPLFree(pszFilename);
   15396          53 :     pszFilename = nullptr;
   15397          53 :     CSLDestroy(papszInfo);
   15398          53 :     papszInfo = nullptr;
   15399          53 :     nOffset = 0;
   15400          53 :     nSize = 0;
   15401          53 :     CPLFree(pabyInlineData);
   15402          53 :     pabyInlineData = nullptr;
   15403          53 : }
   15404             : 
   15405             : /************************************************************************/
   15406             : /*          GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo()          */
   15407             : /************************************************************************/
   15408             : 
   15409           4 : GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo(
   15410           4 :     const GDALMDArrayRawBlockInfo &other)
   15411           4 :     : pszFilename(other.pszFilename ? CPLStrdup(other.pszFilename) : nullptr),
   15412           4 :       nOffset(other.nOffset), nSize(other.nSize),
   15413           4 :       papszInfo(CSLDuplicate(other.papszInfo)), pabyInlineData(nullptr)
   15414             : {
   15415           4 :     if (other.pabyInlineData)
   15416             :     {
   15417           3 :         pabyInlineData = static_cast<GByte *>(
   15418           3 :             VSI_MALLOC_VERBOSE(static_cast<size_t>(other.nSize)));
   15419           3 :         if (pabyInlineData)
   15420           3 :             memcpy(pabyInlineData, other.pabyInlineData,
   15421           3 :                    static_cast<size_t>(other.nSize));
   15422             :     }
   15423           4 : }
   15424             : 
   15425             : /************************************************************************/
   15426             : /*                 GDALMDArrayRawBlockInfo::operator=()                 */
   15427             : /************************************************************************/
   15428             : 
   15429             : GDALMDArrayRawBlockInfo &
   15430           7 : GDALMDArrayRawBlockInfo::operator=(const GDALMDArrayRawBlockInfo &other)
   15431             : {
   15432           7 :     if (this != &other)
   15433             :     {
   15434           5 :         CPLFree(pszFilename);
   15435           5 :         pszFilename =
   15436           5 :             other.pszFilename ? CPLStrdup(other.pszFilename) : nullptr;
   15437           5 :         nOffset = other.nOffset;
   15438           5 :         nSize = other.nSize;
   15439           5 :         CSLDestroy(papszInfo);
   15440           5 :         papszInfo = CSLDuplicate(other.papszInfo);
   15441           5 :         CPLFree(pabyInlineData);
   15442           5 :         pabyInlineData = nullptr;
   15443           5 :         if (other.pabyInlineData)
   15444             :         {
   15445           4 :             pabyInlineData = static_cast<GByte *>(
   15446           4 :                 VSI_MALLOC_VERBOSE(static_cast<size_t>(other.nSize)));
   15447           4 :             if (pabyInlineData)
   15448           4 :                 memcpy(pabyInlineData, other.pabyInlineData,
   15449           4 :                        static_cast<size_t>(other.nSize));
   15450             :         }
   15451             :     }
   15452           7 :     return *this;
   15453             : }
   15454             : 
   15455             : /************************************************************************/
   15456             : /*          GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo()          */
   15457             : /************************************************************************/
   15458             : 
   15459           2 : GDALMDArrayRawBlockInfo::GDALMDArrayRawBlockInfo(
   15460           2 :     GDALMDArrayRawBlockInfo &&other)
   15461           2 :     : pszFilename(other.pszFilename), nOffset(other.nOffset),
   15462           2 :       nSize(other.nSize), papszInfo(other.papszInfo),
   15463           2 :       pabyInlineData(other.pabyInlineData)
   15464             : {
   15465           2 :     other.pszFilename = nullptr;
   15466           2 :     other.papszInfo = nullptr;
   15467           2 :     other.pabyInlineData = nullptr;
   15468           2 : }
   15469             : 
   15470             : /************************************************************************/
   15471             : /*                 GDALMDArrayRawBlockInfo::operator=()                 */
   15472             : /************************************************************************/
   15473             : 
   15474             : GDALMDArrayRawBlockInfo &
   15475           2 : GDALMDArrayRawBlockInfo::operator=(GDALMDArrayRawBlockInfo &&other)
   15476             : {
   15477           2 :     if (this != &other)
   15478             :     {
   15479           2 :         std::swap(pszFilename, other.pszFilename);
   15480           2 :         nOffset = other.nOffset;
   15481           2 :         nSize = other.nSize;
   15482           2 :         std::swap(papszInfo, other.papszInfo);
   15483           2 :         std::swap(pabyInlineData, other.pabyInlineData);
   15484             :     }
   15485           2 :     return *this;
   15486             : }
   15487             : 
   15488             : //! @endcond
   15489             : 
   15490             : /************************************************************************/
   15491             : /*                    GDALMDArray::GetRawBlockInfo()                    */
   15492             : /************************************************************************/
   15493             : 
   15494             : /** Return information on a raw block.
   15495             :  *
   15496             :  * The block coordinates must be between 0 and
   15497             :  * (GetDimensions()[i]->GetSize() / GetBlockSize()[i]) - 1, for all i between
   15498             :  * 0 and GetDimensionCount()-1.
   15499             :  *
   15500             :  * If the queried block has valid coordinates but is missing in the dataset,
   15501             :  * all fields of info will be set to 0/nullptr, but the function will return
   15502             :  * true.
   15503             :  *
   15504             :  * This method is only implemented by a subset of drivers. The base
   15505             :  * implementation just returns false and empty info.
   15506             :  *
   15507             :  * The values returned in psBlockInfo->papszInfo are driver dependent.
   15508             :  *
   15509             :  * For multi-byte data types, drivers should return a "ENDIANNESS" key whose
   15510             :  * value is "LITTLE" or "BIG".
   15511             :  *
   15512             :  * For HDF5 and netCDF 4, the potential keys are "COMPRESSION" (possible values
   15513             :  * "DEFLATE" or "SZIP") and "FILTER" (if several filters, names are
   15514             :  * comma-separated)
   15515             :  *
   15516             :  * For ZARR, the potential keys are "COMPRESSOR" (value is the JSON encoded
   15517             :  * content from the array definition), "FILTERS" (for Zarr V2, value is JSON
   15518             :  * encoded content) and "TRANSPOSE_ORDER" (value is a string like
   15519             :  * "[idx0,...,idxN]" with the permutation).
   15520             :  *
   15521             :  * For VRT, the potential keys are the ones of the underlying source(s). Note
   15522             :  * that GetRawBlockInfo() on VRT only works when the VRT declares a block size,
   15523             :  * that for each queried VRT block, there is one and only one source that
   15524             :  * is used to fill the VRT block and that the block size of this source is
   15525             :  * exactly the one of the VRT block.
   15526             :  *
   15527             :  * This is the same as C function GDALMDArrayGetRawBlockInfo().
   15528             :  *
   15529             :  * @param panBlockCoordinates array of GetDimensionCount() values with the block
   15530             :  *                            coordinates.
   15531             :  * @param[out] info structure to fill with block information.
   15532             :  * @return true in case of success, or false if an error occurs.
   15533             :  * @since 3.12
   15534             :  */
   15535           0 : bool GDALMDArray::GetRawBlockInfo(const uint64_t *panBlockCoordinates,
   15536             :                                   GDALMDArrayRawBlockInfo &info) const
   15537             : {
   15538             :     (void)panBlockCoordinates;
   15539           0 :     info.clear();
   15540           0 :     return false;
   15541             : }
   15542             : 
   15543             : /************************************************************************/
   15544             : /*                     GDALMDArrayGetRawBlockInfo()                     */
   15545             : /************************************************************************/
   15546             : 
   15547             : /** Return information on a raw block.
   15548             :  *
   15549             :  * The block coordinates must be between 0 and
   15550             :  * (GetDimensions()[i]->GetSize() / GetBlockSize()[i]) - 1, for all i between
   15551             :  * 0 and GetDimensionCount()-1.
   15552             :  *
   15553             :  * If the queried block has valid coordinates but is missing in the dataset,
   15554             :  * all fields of info will be set to 0/nullptr, but the function will return
   15555             :  * true.
   15556             :  *
   15557             :  * This method is only implemented by a subset of drivers. The base
   15558             :  * implementation just returns false and empty info.
   15559             :  *
   15560             :  * The values returned in psBlockInfo->papszInfo are driver dependent.
   15561             :  *
   15562             :  * For multi-byte data types, drivers should return a "ENDIANNESS" key whose
   15563             :  * value is "LITTLE" or "BIG".
   15564             :  *
   15565             :  * For HDF5 and netCDF 4, the potential keys are "COMPRESSION" (possible values
   15566             :  * "DEFLATE" or "SZIP") and "FILTER" (if several filters, names are
   15567             :  * comma-separated)
   15568             :  *
   15569             :  * For ZARR, the potential keys are "COMPRESSOR" (value is the JSON encoded
   15570             :  * content from the array definition), "FILTERS" (for Zarr V2, value is JSON
   15571             :  * encoded content) and "TRANSPOSE_ORDER" (value is a string like
   15572             :  * "[idx0,...,idxN]" with the permutation).
   15573             :  *
   15574             :  * For VRT, the potential keys are the ones of the underlying source(s). Note
   15575             :  * that GetRawBlockInfo() on VRT only works when the VRT declares a block size,
   15576             :  * that for each queried VRT block, there is one and only one source that
   15577             :  * is used to fill the VRT block and that the block size of this source is
   15578             :  * exactly the one of the VRT block.
   15579             :  *
   15580             :  * This is the same as C++ method GDALMDArray::GetRawBlockInfo().
   15581             :  *
   15582             :  * @param hArray handle to array.
   15583             :  * @param panBlockCoordinates array of GetDimensionCount() values with the block
   15584             :  *                            coordinates.
   15585             :  * @param[out] psBlockInfo structure to fill with block information.
   15586             :  *                         Must be allocated with GDALMDArrayRawBlockInfoCreate(),
   15587             :  *                         and freed with GDALMDArrayRawBlockInfoRelease().
   15588             :  * @return true in case of success, or false if an error occurs.
   15589             :  * @since 3.12
   15590             :  */
   15591          21 : bool GDALMDArrayGetRawBlockInfo(GDALMDArrayH hArray,
   15592             :                                 const uint64_t *panBlockCoordinates,
   15593             :                                 GDALMDArrayRawBlockInfo *psBlockInfo)
   15594             : {
   15595          21 :     VALIDATE_POINTER1(hArray, __func__, false);
   15596          21 :     VALIDATE_POINTER1(panBlockCoordinates, __func__, false);
   15597          21 :     VALIDATE_POINTER1(psBlockInfo, __func__, false);
   15598          21 :     return hArray->m_poImpl->GetRawBlockInfo(panBlockCoordinates, *psBlockInfo);
   15599             : }
   15600             : 
   15601             : /************************************************************************/
   15602             : /*                   GDALMDArrayRawBlockInfoCreate()                    */
   15603             : /************************************************************************/
   15604             : 
   15605             : /** Allocate a new instance of GDALMDArrayRawBlockInfo.
   15606             :  *
   15607             :  * Returned pointer must be freed with GDALMDArrayRawBlockInfoRelease().
   15608             :  *
   15609             :  * @since 3.12
   15610             :  */
   15611          21 : GDALMDArrayRawBlockInfo *GDALMDArrayRawBlockInfoCreate(void)
   15612             : {
   15613          21 :     return new GDALMDArrayRawBlockInfo();
   15614             : }
   15615             : 
   15616             : /************************************************************************/
   15617             : /*                   GDALMDArrayRawBlockInfoRelease()                   */
   15618             : /************************************************************************/
   15619             : 
   15620             : /** Free an instance of GDALMDArrayRawBlockInfo.
   15621             :  *
   15622             :  * @since 3.12
   15623             :  */
   15624          21 : void GDALMDArrayRawBlockInfoRelease(GDALMDArrayRawBlockInfo *psBlockInfo)
   15625             : {
   15626          21 :     delete psBlockInfo;
   15627          21 : }
   15628             : 
   15629             : /************************************************************************/
   15630             : /*                   GDALMDArray::GetOverviewCount()                    */
   15631             : /************************************************************************/
   15632             : 
   15633             : /**
   15634             :  * \brief Return the number of overview arrays available.
   15635             :  *
   15636             :  * This method is the same as the C function GDALMDArrayGetOverviewCount().
   15637             :  *
   15638             :  * @return overview count, zero if none.
   15639             :  *
   15640             :  * @since 3.13
   15641             :  */
   15642             : 
   15643         115 : int GDALMDArray::GetOverviewCount() const
   15644             : {
   15645         115 :     return 0;
   15646             : }
   15647             : 
   15648             : /************************************************************************/
   15649             : /*                    GDALMDArrayGetOverviewCount()                     */
   15650             : /************************************************************************/
   15651             : /**
   15652             :  * \brief Return the number of overview arrays available.
   15653             :  *
   15654             :  * This method is the same as the C++ method GDALMDArray::GetOverviewCount().
   15655             :  *
   15656             :  * @param hArray Array.
   15657             :  * @return overview count, zero if none.
   15658             :  *
   15659             :  * @since 3.13
   15660             :  */
   15661             : 
   15662         113 : int GDALMDArrayGetOverviewCount(GDALMDArrayH hArray)
   15663             : {
   15664         113 :     VALIDATE_POINTER1(hArray, __func__, 0);
   15665         113 :     return hArray->m_poImpl->GetOverviewCount();
   15666             : }
   15667             : 
   15668             : /************************************************************************/
   15669             : /*                      GDALMDArray::GetOverview()                      */
   15670             : /************************************************************************/
   15671             : 
   15672             : /**
   15673             :  * \brief Get overview array object.
   15674             :  *
   15675             :  * This method is the same as the C function GDALMDArrayGetOverview().
   15676             :  *
   15677             :  * @param nIdx overview index between 0 and GetOverviewCount()-1.
   15678             :  *
   15679             :  * @return overview GDALMDArray, or nullptr
   15680             :  *
   15681             :  * @since 3.13
   15682             :  */
   15683             : 
   15684          70 : std::shared_ptr<GDALMDArray> GDALMDArray::GetOverview(int nIdx) const
   15685             : {
   15686             :     (void)nIdx;
   15687          70 :     return nullptr;
   15688             : }
   15689             : 
   15690             : /************************************************************************/
   15691             : /*                       GDALMDArrayGetOverview()                       */
   15692             : /************************************************************************/
   15693             : 
   15694             : /**
   15695             :  * \brief Get overview array object.
   15696             :  *
   15697             :  * This method is the same as the C++ method GDALMDArray::GetOverview().
   15698             :  *
   15699             :  * @param hArray Array.
   15700             :  * @param nIdx overview index between 0 and GDALMDArrayGetOverviewCount()-1.
   15701             :  *
   15702             :  * @return overview GDALMDArray, or nullptr.
   15703             :  * Must be released with GDALMDArrayRelease()
   15704             :  *
   15705             :  * @since 3.13
   15706             :  */
   15707             : 
   15708         124 : GDALMDArrayH GDALMDArrayGetOverview(GDALMDArrayH hArray, int nIdx)
   15709             : {
   15710         124 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   15711         248 :     auto poOverview = hArray->m_poImpl->GetOverview(nIdx);
   15712         124 :     if (!poOverview)
   15713          89 :         return nullptr;
   15714          35 :     return new GDALMDArrayHS(poOverview);
   15715             : }
   15716             : 
   15717             : /************************************************************************/
   15718             : /*                    GDALMDArray::BuildOverviews()                     */
   15719             : /************************************************************************/
   15720             : 
   15721             : /** Build overviews for this array.
   15722             :  *
   15723             :  * Creates reduced resolution copies of this array using the specified
   15724             :  * resampling method. The driver is responsible for storing the overview
   15725             :  * arrays and any associated metadata (e.g., multiscales convention for Zarr).
   15726             :  *
   15727             :  * For arrays with more than 2 dimensions, only the spatial dimensions
   15728             :  * (last two by default, or as specified by the spatial:dimensions
   15729             :  * attribute) are downsampled. Non-spatial dimensions are preserved.
   15730             :  *
   15731             :  * Overview factors need not be sorted; the implementation will sort and
   15732             :  * deduplicate them. Each level is resampled sequentially from the
   15733             :  * previous level (e.g., 4x is built from 2x, not from the base).
   15734             :  *
   15735             :  * This method can also be invoked via GDALDataset::BuildOverviews()
   15736             :  * when the dataset was obtained through GDALMDArray::AsClassicDataset().
   15737             :  *
   15738             :  * @note The Zarr v3 implementation replaces all existing overviews on each
   15739             :  * call, unlike GDALDataset::BuildOverviews() which may add new levels.
   15740             :  *
   15741             :  * @note Currently only implemented by the Zarr v3 driver.
   15742             :  *
   15743             :  * @param pszResampling Resampling method name (e.g., "NEAREST", "AVERAGE").
   15744             :  *                      If nullptr or empty, defaults to "NEAREST".
   15745             :  * @param nOverviews Number of overview levels to build. Pass 0 to remove
   15746             :  *                   all existing overviews.
   15747             :  * @param panOverviewList Array of overview decimation factors (e.g., 2, 4, 8).
   15748             :  *                        Each factor must be >= 2. May be nullptr when
   15749             :  *                        nOverviews is 0.
   15750             :  * @param pfnProgress Progress callback, or nullptr.
   15751             :  * @param pProgressData Progress callback user data.
   15752             :  * @param papszOptions Driver-specific options, or nullptr.
   15753             :  * @return CE_None on success, CE_Failure otherwise.
   15754             :  * @since GDAL 3.13
   15755             :  */
   15756           1 : CPLErr GDALMDArray::BuildOverviews(CPL_UNUSED const char *pszResampling,
   15757             :                                    CPL_UNUSED int nOverviews,
   15758             :                                    CPL_UNUSED const int *panOverviewList,
   15759             :                                    CPL_UNUSED GDALProgressFunc pfnProgress,
   15760             :                                    CPL_UNUSED void *pProgressData,
   15761             :                                    CPL_UNUSED CSLConstList papszOptions)
   15762             : {
   15763           1 :     CPLError(CE_Failure, CPLE_NotSupported,
   15764             :              "BuildOverviews() not supported by this driver");
   15765           1 :     return CE_Failure;
   15766             : }
   15767             : 
   15768             : /************************************************************************/
   15769             : /*                     GDALMDArrayBuildOverviews()                      */
   15770             : /************************************************************************/
   15771             : 
   15772             : /** \brief Build overviews for a multidimensional array.
   15773             :  *
   15774             :  * This is the same as the C++ method GDALMDArray::BuildOverviews().
   15775             :  *
   15776             :  * @since GDAL 3.13
   15777             :  */
   15778          13 : CPLErr GDALMDArrayBuildOverviews(GDALMDArrayH hArray, const char *pszResampling,
   15779             :                                  int nOverviews, const int *panOverviewList,
   15780             :                                  GDALProgressFunc pfnProgress,
   15781             :                                  void *pProgressData, CSLConstList papszOptions)
   15782             : {
   15783          13 :     VALIDATE_POINTER1(hArray, __func__, CE_Failure);
   15784          26 :     return hArray->m_poImpl->BuildOverviews(pszResampling, nOverviews,
   15785             :                                             panOverviewList, pfnProgress,
   15786          13 :                                             pProgressData, papszOptions);
   15787             : }

Generated by: LCOV version 1.14