LCOV - code coverage report
Current view: top level - gcore - gdalmultidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 4609 5109 90.2 %
Date: 2024-11-21 22:18:42 Functions: 466 536 86.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * $Id$
       3             :  *
       4             :  * Name:     gdalmultidim.cpp
       5             :  * Project:  GDAL Core
       6             :  * Purpose:  GDAL Core C++/Private implementation for multidimensional support
       7             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       8             :  *
       9             :  ******************************************************************************
      10             :  * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include <assert.h>
      16             : #include <algorithm>
      17             : #include <limits>
      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 "gdal_priv.h"
      28             : #include "gdal_pam.h"
      29             : #include "gdal_utils.h"
      30             : #include "cpl_safemaths.hpp"
      31             : #include "memmultidim.h"
      32             : #include "ogrsf_frmts.h"
      33             : #include "gdalmultidim_priv.h"
      34             : 
      35             : #if defined(__clang__) || defined(_MSC_VER)
      36             : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
      37             : #endif
      38             : 
      39             : /************************************************************************/
      40             : /*                       GDALMDArrayUnscaled                            */
      41             : /************************************************************************/
      42             : 
      43             : class GDALMDArrayUnscaled final : public GDALPamMDArray
      44             : {
      45             :   private:
      46             :     std::shared_ptr<GDALMDArray> m_poParent{};
      47             :     const GDALExtendedDataType m_dt;
      48             :     bool m_bHasNoData;
      49             :     const double m_dfScale;
      50             :     const double m_dfOffset;
      51             :     std::vector<GByte> m_abyRawNoData{};
      52             : 
      53             :   protected:
      54          13 :     explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
      55             :                                  double dfScale, double dfOffset,
      56             :                                  double dfOverriddenDstNodata, GDALDataType eDT)
      57          26 :         : GDALAbstractMDArray(std::string(),
      58          26 :                               "Unscaled view of " + poParent->GetFullName()),
      59             :           GDALPamMDArray(
      60          26 :               std::string(), "Unscaled view of " + poParent->GetFullName(),
      61          26 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
      62          13 :           m_poParent(std::move(poParent)),
      63             :           m_dt(GDALExtendedDataType::Create(eDT)),
      64          13 :           m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
      65          78 :           m_dfScale(dfScale), m_dfOffset(dfOffset)
      66             :     {
      67          13 :         m_abyRawNoData.resize(m_dt.GetSize());
      68             :         const auto eNonComplexDT =
      69          13 :             GDALGetNonComplexDataType(m_dt.GetNumericDataType());
      70          26 :         GDALCopyWords(&dfOverriddenDstNodata, GDT_Float64, 0,
      71          13 :                       m_abyRawNoData.data(), eNonComplexDT,
      72             :                       GDALGetDataTypeSizeBytes(eNonComplexDT),
      73          13 :                       GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
      74          13 :     }
      75             : 
      76             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
      77             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      78             :                const GDALExtendedDataType &bufferDataType,
      79             :                void *pDstBuffer) const override;
      80             : 
      81             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
      82             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      83             :                 const GDALExtendedDataType &bufferDataType,
      84             :                 const void *pSrcBuffer) override;
      85             : 
      86           0 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
      87             :                      CSLConstList papszOptions) const override
      88             :     {
      89           0 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
      90             :     }
      91             : 
      92             :   public:
      93             :     static std::shared_ptr<GDALMDArrayUnscaled>
      94          13 :     Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
      95             :            double dfOffset, double dfDstNodata, GDALDataType eDT)
      96             :     {
      97             :         auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
      98          13 :             poParent, dfScale, dfOffset, dfDstNodata, eDT)));
      99          13 :         newAr->SetSelf(newAr);
     100          13 :         return newAr;
     101             :     }
     102             : 
     103           1 :     bool IsWritable() const override
     104             :     {
     105           1 :         return m_poParent->IsWritable();
     106             :     }
     107             : 
     108          15 :     const std::string &GetFilename() const override
     109             :     {
     110          15 :         return m_poParent->GetFilename();
     111             :     }
     112             : 
     113             :     const std::vector<std::shared_ptr<GDALDimension>> &
     114         220 :     GetDimensions() const override
     115             :     {
     116         220 :         return m_poParent->GetDimensions();
     117             :     }
     118             : 
     119         103 :     const GDALExtendedDataType &GetDataType() const override
     120             :     {
     121         103 :         return m_dt;
     122             :     }
     123             : 
     124           1 :     const std::string &GetUnit() const override
     125             :     {
     126           1 :         return m_poParent->GetUnit();
     127             :     }
     128             : 
     129           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
     130             :     {
     131           1 :         return m_poParent->GetSpatialRef();
     132             :     }
     133             : 
     134           6 :     const void *GetRawNoDataValue() const override
     135             :     {
     136           6 :         return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
     137             :     }
     138             : 
     139           1 :     bool SetRawNoDataValue(const void *pRawNoData) override
     140             :     {
     141           1 :         m_bHasNoData = true;
     142           1 :         memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
     143           1 :         return true;
     144             :     }
     145             : 
     146           4 :     std::vector<GUInt64> GetBlockSize() const override
     147             :     {
     148           4 :         return m_poParent->GetBlockSize();
     149             :     }
     150             : 
     151             :     std::shared_ptr<GDALAttribute>
     152           0 :     GetAttribute(const std::string &osName) const override
     153             :     {
     154           0 :         return m_poParent->GetAttribute(osName);
     155             :     }
     156             : 
     157             :     std::vector<std::shared_ptr<GDALAttribute>>
     158           1 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
     159             :     {
     160           1 :         return m_poParent->GetAttributes(papszOptions);
     161             :     }
     162             : 
     163           0 :     bool SetUnit(const std::string &osUnit) override
     164             :     {
     165           0 :         return m_poParent->SetUnit(osUnit);
     166             :     }
     167             : 
     168           0 :     bool SetSpatialRef(const OGRSpatialReference *poSRS) override
     169             :     {
     170           0 :         return m_poParent->SetSpatialRef(poSRS);
     171             :     }
     172             : 
     173             :     std::shared_ptr<GDALAttribute>
     174           1 :     CreateAttribute(const std::string &osName,
     175             :                     const std::vector<GUInt64> &anDimensions,
     176             :                     const GDALExtendedDataType &oDataType,
     177             :                     CSLConstList papszOptions = nullptr) override
     178             :     {
     179           1 :         return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
     180           1 :                                            papszOptions);
     181             :     }
     182             : };
     183             : 
     184             : /************************************************************************/
     185             : /*                         ~GDALIHasAttribute()                         */
     186             : /************************************************************************/
     187             : 
     188             : GDALIHasAttribute::~GDALIHasAttribute() = default;
     189             : 
     190             : /************************************************************************/
     191             : /*                            GetAttribute()                            */
     192             : /************************************************************************/
     193             : 
     194             : /** Return an attribute by its name.
     195             :  *
     196             :  * If the attribute does not exist, nullptr should be silently returned.
     197             :  *
     198             :  * @note Driver implementation: this method will fallback to
     199             :  * GetAttributeFromAttributes() is not explicitly implemented
     200             :  *
     201             :  * Drivers known to implement it for groups and arrays: MEM, netCDF.
     202             :  *
     203             :  * This is the same as the C function GDALGroupGetAttribute() or
     204             :  * GDALMDArrayGetAttribute().
     205             :  *
     206             :  * @param osName Attribute name
     207             :  * @return the attribute, or nullptr if it does not exist or an error occurred.
     208             :  */
     209             : std::shared_ptr<GDALAttribute>
     210        1022 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
     211             : {
     212        1022 :     return GetAttributeFromAttributes(osName);
     213             : }
     214             : 
     215             : /************************************************************************/
     216             : /*                       GetAttributeFromAttributes()                   */
     217             : /************************************************************************/
     218             : 
     219             : /** Possible fallback implementation for GetAttribute() using GetAttributes().
     220             :  */
     221             : std::shared_ptr<GDALAttribute>
     222        1022 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
     223             : {
     224        2044 :     auto attrs(GetAttributes());
     225        5357 :     for (const auto &attr : attrs)
     226             :     {
     227        5051 :         if (attr->GetName() == osName)
     228         716 :             return attr;
     229             :     }
     230         306 :     return nullptr;
     231             : }
     232             : 
     233             : /************************************************************************/
     234             : /*                           GetAttributes()                            */
     235             : /************************************************************************/
     236             : 
     237             : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
     238             :  *
     239             :  * If the attribute does not exist, nullptr should be silently returned.
     240             :  *
     241             :  * @note Driver implementation: optionally implemented. If implemented,
     242             :  * GetAttribute() should also be implemented.
     243             :  *
     244             :  * Drivers known to implement it for groups and arrays: MEM, netCDF.
     245             :  *
     246             :  * This is the same as the C function GDALGroupGetAttributes() or
     247             :  * GDALMDArrayGetAttributes().
     248             : 
     249             :  * @param papszOptions Driver specific options determining how attributes
     250             :  * should be retrieved. Pass nullptr for default behavior.
     251             :  *
     252             :  * @return the attributes.
     253             :  */
     254             : std::vector<std::shared_ptr<GDALAttribute>>
     255          41 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
     256             : {
     257          41 :     return {};
     258             : }
     259             : 
     260             : /************************************************************************/
     261             : /*                             CreateAttribute()                         */
     262             : /************************************************************************/
     263             : 
     264             : /** Create an attribute within a GDALMDArray or GDALGroup.
     265             :  *
     266             :  * The attribute might not be "physically" created until a value is written
     267             :  * into it.
     268             :  *
     269             :  * Optionally implemented.
     270             :  *
     271             :  * Drivers known to implement it: MEM, netCDF
     272             :  *
     273             :  * This is the same as the C function GDALGroupCreateAttribute() or
     274             :  * GDALMDArrayCreateAttribute()
     275             :  *
     276             :  * @param osName Attribute name.
     277             :  * @param anDimensions List of dimension sizes, ordered from the slowest varying
     278             :  *                     dimension first to the fastest varying dimension last.
     279             :  *                     Empty for a scalar attribute (common case)
     280             :  * @param oDataType  Attribute data type.
     281             :  * @param papszOptions Driver specific options determining how the attribute.
     282             :  * should be created.
     283             :  *
     284             :  * @return the new attribute, or nullptr if case of error
     285             :  */
     286           0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
     287             :     CPL_UNUSED const std::string &osName,
     288             :     CPL_UNUSED const std::vector<GUInt64> &anDimensions,
     289             :     CPL_UNUSED const GDALExtendedDataType &oDataType,
     290             :     CPL_UNUSED CSLConstList papszOptions)
     291             : {
     292           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     293             :              "CreateAttribute() not implemented");
     294           0 :     return nullptr;
     295             : }
     296             : 
     297             : /************************************************************************/
     298             : /*                          DeleteAttribute()                           */
     299             : /************************************************************************/
     300             : 
     301             : /** Delete an attribute from a GDALMDArray or GDALGroup.
     302             :  *
     303             :  * Optionally implemented.
     304             :  *
     305             :  * After this call, if a previously obtained instance of the deleted object
     306             :  * is still alive, no method other than for freeing it should be invoked.
     307             :  *
     308             :  * Drivers known to implement it: MEM, netCDF
     309             :  *
     310             :  * This is the same as the C function GDALGroupDeleteAttribute() or
     311             :  * GDALMDArrayDeleteAttribute()
     312             :  *
     313             :  * @param osName Attribute name.
     314             :  * @param papszOptions Driver specific options determining how the attribute.
     315             :  * should be deleted.
     316             :  *
     317             :  * @return true in case of success
     318             :  * @since GDAL 3.8
     319             :  */
     320           0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
     321             :                                         CPL_UNUSED CSLConstList papszOptions)
     322             : {
     323           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     324             :              "DeleteAttribute() not implemented");
     325           0 :     return false;
     326             : }
     327             : 
     328             : /************************************************************************/
     329             : /*                            GDALGroup()                               */
     330             : /************************************************************************/
     331             : 
     332             : //! @cond Doxygen_Suppress
     333        6670 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
     334        6670 :                      const std::string &osContext)
     335        6670 :     : m_osName(osParentName.empty() ? "/" : osName),
     336             :       m_osFullName(
     337       13340 :           !osParentName.empty()
     338       10244 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
     339             :               : "/"),
     340       16914 :       m_osContext(osContext)
     341             : {
     342        6670 : }
     343             : 
     344             : //! @endcond
     345             : 
     346             : /************************************************************************/
     347             : /*                            ~GDALGroup()                              */
     348             : /************************************************************************/
     349             : 
     350             : GDALGroup::~GDALGroup() = default;
     351             : 
     352             : /************************************************************************/
     353             : /*                          GetMDArrayNames()                           */
     354             : /************************************************************************/
     355             : 
     356             : /** Return the list of multidimensional array names contained in this group.
     357             :  *
     358             :  * @note Driver implementation: optionally implemented. If implemented,
     359             :  * OpenMDArray() should also be implemented.
     360             :  *
     361             :  * Drivers known to implement it: MEM, netCDF.
     362             :  *
     363             :  * This is the same as the C function GDALGroupGetMDArrayNames().
     364             :  *
     365             :  * @param papszOptions Driver specific options determining how arrays
     366             :  * should be retrieved. Pass nullptr for default behavior.
     367             :  *
     368             :  * @return the array names.
     369             :  */
     370             : std::vector<std::string>
     371           0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
     372             : {
     373           0 :     return {};
     374             : }
     375             : 
     376             : /************************************************************************/
     377             : /*                            OpenMDArray()                             */
     378             : /************************************************************************/
     379             : 
     380             : /** Open and return a multidimensional array.
     381             :  *
     382             :  * @note Driver implementation: optionally implemented. If implemented,
     383             :  * GetMDArrayNames() should also be implemented.
     384             :  *
     385             :  * Drivers known to implement it: MEM, netCDF.
     386             :  *
     387             :  * This is the same as the C function GDALGroupOpenMDArray().
     388             :  *
     389             :  * @param osName Array name.
     390             :  * @param papszOptions Driver specific options determining how the array should
     391             :  * be opened.  Pass nullptr for default behavior.
     392             :  *
     393             :  * @return the array, or nullptr.
     394             :  */
     395             : std::shared_ptr<GDALMDArray>
     396           0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
     397             :                        CPL_UNUSED CSLConstList papszOptions) const
     398             : {
     399           0 :     return nullptr;
     400             : }
     401             : 
     402             : /************************************************************************/
     403             : /*                           GetGroupNames()                            */
     404             : /************************************************************************/
     405             : 
     406             : /** Return the list of sub-groups contained in this group.
     407             :  *
     408             :  * @note Driver implementation: optionally implemented. If implemented,
     409             :  * OpenGroup() should also be implemented.
     410             :  *
     411             :  * Drivers known to implement it: MEM, netCDF.
     412             :  *
     413             :  * This is the same as the C function GDALGroupGetGroupNames().
     414             :  *
     415             :  * @param papszOptions Driver specific options determining how groups
     416             :  * should be retrieved. Pass nullptr for default behavior.
     417             :  *
     418             :  * @return the group names.
     419             :  */
     420             : std::vector<std::string>
     421           4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
     422             : {
     423           4 :     return {};
     424             : }
     425             : 
     426             : /************************************************************************/
     427             : /*                             OpenGroup()                              */
     428             : /************************************************************************/
     429             : 
     430             : /** Open and return a sub-group.
     431             :  *
     432             :  * @note Driver implementation: optionally implemented. If implemented,
     433             :  * GetGroupNames() should also be implemented.
     434             :  *
     435             :  * Drivers known to implement it: MEM, netCDF.
     436             :  *
     437             :  * This is the same as the C function GDALGroupOpenGroup().
     438             :  *
     439             :  * @param osName Sub-group name.
     440             :  * @param papszOptions Driver specific options determining how the sub-group
     441             :  * should be opened.  Pass nullptr for default behavior.
     442             :  *
     443             :  * @return the group, or nullptr.
     444             :  */
     445             : std::shared_ptr<GDALGroup>
     446           4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
     447             :                      CPL_UNUSED CSLConstList papszOptions) const
     448             : {
     449           4 :     return nullptr;
     450             : }
     451             : 
     452             : /************************************************************************/
     453             : /*                        GetVectorLayerNames()                         */
     454             : /************************************************************************/
     455             : 
     456             : /** Return the list of layer names contained in this group.
     457             :  *
     458             :  * @note Driver implementation: optionally implemented. If implemented,
     459             :  * OpenVectorLayer() should also be implemented.
     460             :  *
     461             :  * Drivers known to implement it: OpenFileGDB, FileGDB
     462             :  *
     463             :  * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
     464             :  * GDALDataset::GetLayer() should then be used.
     465             :  *
     466             :  * This is the same as the C function GDALGroupGetVectorLayerNames().
     467             :  *
     468             :  * @param papszOptions Driver specific options determining how layers
     469             :  * should be retrieved. Pass nullptr for default behavior.
     470             :  *
     471             :  * @return the vector layer names.
     472             :  * @since GDAL 3.4
     473             :  */
     474             : std::vector<std::string>
     475           1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
     476             : {
     477           1 :     return {};
     478             : }
     479             : 
     480             : /************************************************************************/
     481             : /*                           OpenVectorLayer()                          */
     482             : /************************************************************************/
     483             : 
     484             : /** Open and return a vector layer.
     485             :  *
     486             :  * Due to the historical ownership of OGRLayer* by GDALDataset*, the
     487             :  * lifetime of the returned OGRLayer* is linked to the one of the owner
     488             :  * dataset (contrary to the general design of this class where objects can be
     489             :  * used independently of the object that returned them)
     490             :  *
     491             :  * @note Driver implementation: optionally implemented. If implemented,
     492             :  * GetVectorLayerNames() should also be implemented.
     493             :  *
     494             :  * Drivers known to implement it: MEM, netCDF.
     495             :  *
     496             :  * This is the same as the C function GDALGroupOpenVectorLayer().
     497             :  *
     498             :  * @param osName Vector layer name.
     499             :  * @param papszOptions Driver specific options determining how the layer should
     500             :  * be opened.  Pass nullptr for default behavior.
     501             :  *
     502             :  * @return the group, or nullptr.
     503             :  */
     504           2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
     505             :                                      CPL_UNUSED CSLConstList papszOptions) const
     506             : {
     507           2 :     return nullptr;
     508             : }
     509             : 
     510             : /************************************************************************/
     511             : /*                             GetDimensions()                          */
     512             : /************************************************************************/
     513             : 
     514             : /** Return the list of dimensions contained in this group and used by its
     515             :  * arrays.
     516             :  *
     517             :  * This is for dimensions that can potentially be used by several arrays.
     518             :  * Not all drivers might implement this. To retrieve the dimensions used by
     519             :  * a specific array, use GDALMDArray::GetDimensions().
     520             :  *
     521             :  * Drivers known to implement it: MEM, netCDF
     522             :  *
     523             :  * This is the same as the C function GDALGroupGetDimensions().
     524             :  *
     525             :  * @param papszOptions Driver specific options determining how groups
     526             :  * should be retrieved. Pass nullptr for default behavior.
     527             :  *
     528             :  * @return the dimensions.
     529             :  */
     530             : std::vector<std::shared_ptr<GDALDimension>>
     531          11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
     532             : {
     533          11 :     return {};
     534             : }
     535             : 
     536             : /************************************************************************/
     537             : /*                         GetStructuralInfo()                          */
     538             : /************************************************************************/
     539             : 
     540             : /** Return structural information on the group.
     541             :  *
     542             :  * This may be the compression, etc..
     543             :  *
     544             :  * The return value should not be freed and is valid until GDALGroup is
     545             :  * released or this function called again.
     546             :  *
     547             :  * This is the same as the C function GDALGroupGetStructuralInfo().
     548             :  */
     549          29 : CSLConstList GDALGroup::GetStructuralInfo() const
     550             : {
     551          29 :     return nullptr;
     552             : }
     553             : 
     554             : /************************************************************************/
     555             : /*                              CreateGroup()                           */
     556             : /************************************************************************/
     557             : 
     558             : /** Create a sub-group within a group.
     559             :  *
     560             :  * Optionally implemented by drivers.
     561             :  *
     562             :  * Drivers known to implement it: MEM, netCDF
     563             :  *
     564             :  * This is the same as the C function GDALGroupCreateGroup().
     565             :  *
     566             :  * @param osName Sub-group name.
     567             :  * @param papszOptions Driver specific options determining how the sub-group
     568             :  * should be created.
     569             :  *
     570             :  * @return the new sub-group, or nullptr in case of error.
     571             :  */
     572             : std::shared_ptr<GDALGroup>
     573           0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
     574             :                        CPL_UNUSED CSLConstList papszOptions)
     575             : {
     576           0 :     CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
     577           0 :     return nullptr;
     578             : }
     579             : 
     580             : /************************************************************************/
     581             : /*                          DeleteGroup()                               */
     582             : /************************************************************************/
     583             : 
     584             : /** Delete a sub-group from a group.
     585             :  *
     586             :  * Optionally implemented.
     587             :  *
     588             :  * After this call, if a previously obtained instance of the deleted object
     589             :  * is still alive, no method other than for freeing it should be invoked.
     590             :  *
     591             :  * Drivers known to implement it: MEM, Zarr
     592             :  *
     593             :  * This is the same as the C function GDALGroupDeleteGroup().
     594             :  *
     595             :  * @param osName Sub-group name.
     596             :  * @param papszOptions Driver specific options determining how the group.
     597             :  * should be deleted.
     598             :  *
     599             :  * @return true in case of success
     600             :  * @since GDAL 3.8
     601             :  */
     602           0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
     603             :                             CPL_UNUSED CSLConstList papszOptions)
     604             : {
     605           0 :     CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
     606           0 :     return false;
     607             : }
     608             : 
     609             : /************************************************************************/
     610             : /*                            CreateDimension()                         */
     611             : /************************************************************************/
     612             : 
     613             : /** Create a dimension within a group.
     614             :  *
     615             :  * @note Driver implementation: drivers supporting CreateDimension() should
     616             :  * implement this method, but do not have necessarily to implement
     617             :  * GDALGroup::GetDimensions().
     618             :  *
     619             :  * Drivers known to implement it: MEM, netCDF
     620             :  *
     621             :  * This is the same as the C function GDALGroupCreateDimension().
     622             :  *
     623             :  * @param osName Dimension name.
     624             :  * @param osType Dimension type (might be empty, and ignored by drivers)
     625             :  * @param osDirection Dimension direction (might be empty, and ignored by
     626             :  * drivers)
     627             :  * @param nSize  Number of values indexed by this dimension. Should be > 0.
     628             :  * @param papszOptions Driver specific options determining how the dimension
     629             :  * should be created.
     630             :  *
     631             :  * @return the new dimension, or nullptr if case of error
     632             :  */
     633           0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
     634             :     CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
     635             :     CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
     636             :     CPL_UNUSED CSLConstList papszOptions)
     637             : {
     638           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     639             :              "CreateDimension() not implemented");
     640           0 :     return nullptr;
     641             : }
     642             : 
     643             : /************************************************************************/
     644             : /*                             CreateMDArray()                          */
     645             : /************************************************************************/
     646             : 
     647             : /** Create a multidimensional array within a group.
     648             :  *
     649             :  * It is recommended that the GDALDimension objects passed in aoDimensions
     650             :  * belong to this group, either by retrieving them with GetDimensions()
     651             :  * or creating a new one with CreateDimension().
     652             :  *
     653             :  * Optionally implemented.
     654             :  *
     655             :  * Drivers known to implement it: MEM, netCDF
     656             :  *
     657             :  * This is the same as the C function GDALGroupCreateMDArray().
     658             :  *
     659             :  * @note Driver implementation: drivers should take into account the possibility
     660             :  * that GDALDimension object passed in aoDimensions might belong to a different
     661             :  * group / dataset / driver and act accordingly.
     662             :  *
     663             :  * @param osName Array name.
     664             :  * @param aoDimensions List of dimensions, ordered from the slowest varying
     665             :  *                     dimension first to the fastest varying dimension last.
     666             :  *                     Might be empty for a scalar array (if supported by
     667             :  * driver)
     668             :  * @param oDataType  Array data type.
     669             :  * @param papszOptions Driver specific options determining how the array
     670             :  * should be created.
     671             :  *
     672             :  * @return the new array, or nullptr if case of error
     673             :  */
     674           0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
     675             :     CPL_UNUSED const std::string &osName,
     676             :     CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
     677             :     CPL_UNUSED const GDALExtendedDataType &oDataType,
     678             :     CPL_UNUSED CSLConstList papszOptions)
     679             : {
     680           0 :     CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
     681           0 :     return nullptr;
     682             : }
     683             : 
     684             : /************************************************************************/
     685             : /*                          DeleteMDArray()                             */
     686             : /************************************************************************/
     687             : 
     688             : /** Delete an array from a group.
     689             :  *
     690             :  * Optionally implemented.
     691             :  *
     692             :  * After this call, if a previously obtained instance of the deleted object
     693             :  * is still alive, no method other than for freeing it should be invoked.
     694             :  *
     695             :  * Drivers known to implement it: MEM, Zarr
     696             :  *
     697             :  * This is the same as the C function GDALGroupDeleteMDArray().
     698             :  *
     699             :  * @param osName Arrayname.
     700             :  * @param papszOptions Driver specific options determining how the array.
     701             :  * should be deleted.
     702             :  *
     703             :  * @return true in case of success
     704             :  * @since GDAL 3.8
     705             :  */
     706           0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
     707             :                               CPL_UNUSED CSLConstList papszOptions)
     708             : {
     709           0 :     CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
     710           0 :     return false;
     711             : }
     712             : 
     713             : /************************************************************************/
     714             : /*                           GetTotalCopyCost()                         */
     715             : /************************************************************************/
     716             : 
     717             : /** Return a total "cost" to copy the group.
     718             :  *
     719             :  * Used as a parameter for CopFrom()
     720             :  */
     721          22 : GUInt64 GDALGroup::GetTotalCopyCost() const
     722             : {
     723          22 :     GUInt64 nCost = COPY_COST;
     724          22 :     nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
     725             : 
     726          44 :     auto groupNames = GetGroupNames();
     727          26 :     for (const auto &name : groupNames)
     728             :     {
     729           8 :         auto subGroup = OpenGroup(name);
     730           4 :         if (subGroup)
     731             :         {
     732           4 :             nCost += subGroup->GetTotalCopyCost();
     733             :         }
     734             :     }
     735             : 
     736          22 :     auto arrayNames = GetMDArrayNames();
     737          61 :     for (const auto &name : arrayNames)
     738             :     {
     739          78 :         auto array = OpenMDArray(name);
     740          39 :         if (array)
     741             :         {
     742          39 :             nCost += array->GetTotalCopyCost();
     743             :         }
     744             :     }
     745          44 :     return nCost;
     746             : }
     747             : 
     748             : /************************************************************************/
     749             : /*                               CopyFrom()                             */
     750             : /************************************************************************/
     751             : 
     752             : /** Copy the content of a group into a new (generally empty) group.
     753             :  *
     754             :  * @param poDstRootGroup Destination root group. Must NOT be nullptr.
     755             :  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
     756             :  *                   of some output drivers this is not recommended)
     757             :  * @param poSrcGroup Source group. Must NOT be nullptr.
     758             :  * @param bStrict Whether to enable strict mode. In strict mode, any error will
     759             :  *                stop the copy. In relaxed mode, the copy will be attempted to
     760             :  *                be pursued.
     761             :  * @param nCurCost  Should be provided as a variable initially set to 0.
     762             :  * @param nTotalCost Total cost from GetTotalCopyCost().
     763             :  * @param pfnProgress Progress callback, or nullptr.
     764             :  * @param pProgressData Progress user data, or nulptr.
     765             :  * @param papszOptions Creation options. Currently, only array creation
     766             :  *                     options are supported. They must be prefixed with
     767             :  * "ARRAY:" . The scope may be further restricted to arrays of a certain
     768             :  *                     dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
     769             :  *                     For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
     770             :  *                     restrict BLOCKSIZE=256,256 to arrays of dimension 2.
     771             :  *                     Restriction to arrays of a given name is done with adding
     772             :  *                     "IF(NAME={name}):" after "ARRAY:". {name} can also be
     773             :  *                     a full qualified name.
     774             :  *                     A non-driver specific ARRAY option, "AUTOSCALE=YES" can
     775             :  * be used to ask (non indexing) variables of type Float32 or Float64 to be
     776             :  * scaled to UInt16 with scale and offset values being computed from the minimum
     777             :  * and maximum of the source array. The integer data type used can be set with
     778             :  *                     AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
     779             :  *
     780             :  * @return true in case of success (or partial success if bStrict == false).
     781             :  */
     782          22 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
     783             :                          GDALDataset *poSrcDS,
     784             :                          const std::shared_ptr<GDALGroup> &poSrcGroup,
     785             :                          bool bStrict, GUInt64 &nCurCost,
     786             :                          const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
     787             :                          void *pProgressData, CSLConstList papszOptions)
     788             : {
     789          22 :     if (pfnProgress == nullptr)
     790           0 :         pfnProgress = GDALDummyProgress;
     791             : 
     792             : #define EXIT_OR_CONTINUE_IF_NULL(x)                                            \
     793             :     if (!(x))                                                                  \
     794             :     {                                                                          \
     795             :         if (bStrict)                                                           \
     796             :             return false;                                                      \
     797             :         continue;                                                              \
     798             :     }                                                                          \
     799             :     (void)0
     800             : 
     801             :     try
     802             :     {
     803          22 :         nCurCost += GDALGroup::COPY_COST;
     804             : 
     805          44 :         const auto srcDims = poSrcGroup->GetDimensions();
     806             :         std::map<std::string, std::shared_ptr<GDALDimension>>
     807          44 :             mapExistingDstDims;
     808          44 :         std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
     809          56 :         for (const auto &dim : srcDims)
     810             :         {
     811             :             auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
     812          34 :                                           dim->GetDirection(), dim->GetSize());
     813          34 :             EXIT_OR_CONTINUE_IF_NULL(dstDim);
     814          34 :             mapExistingDstDims[dim->GetName()] = std::move(dstDim);
     815          68 :             auto poIndexingVarSrc(dim->GetIndexingVariable());
     816          34 :             if (poIndexingVarSrc)
     817             :             {
     818             :                 mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
     819          16 :                                                        ->GetName()] =
     820          32 :                     dim->GetName();
     821             :             }
     822             :         }
     823             : 
     824          44 :         auto attrs = poSrcGroup->GetAttributes();
     825          28 :         for (const auto &attr : attrs)
     826             :         {
     827             :             auto dstAttr =
     828           6 :                 CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
     829          12 :                                 attr->GetDataType());
     830           6 :             EXIT_OR_CONTINUE_IF_NULL(dstAttr);
     831           6 :             auto raw(attr->ReadAsRaw());
     832           6 :             if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
     833           0 :                 return false;
     834             :         }
     835          22 :         if (!attrs.empty())
     836             :         {
     837           4 :             nCurCost += attrs.size() * GDALAttribute::COPY_COST;
     838           4 :             if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
     839           0 :                 return false;
     840             :         }
     841             : 
     842             :         const auto CopyArray =
     843          39 :             [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
     844             :              &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
     845             :              papszOptions, bStrict, &nCurCost,
     846         355 :              nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
     847             :         {
     848             :             // Map source dimensions to target dimensions
     849          78 :             std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
     850          39 :             const auto &srcArrayDims(srcArray->GetDimensions());
     851          99 :             for (const auto &dim : srcArrayDims)
     852             :             {
     853             :                 auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
     854          60 :                     dim->GetFullName());
     855          60 :                 if (dstDim && dstDim->GetSize() == dim->GetSize())
     856             :                 {
     857          50 :                     dstArrayDims.emplace_back(dstDim);
     858             :                 }
     859             :                 else
     860             :                 {
     861          10 :                     auto oIter = mapExistingDstDims.find(dim->GetName());
     862          19 :                     if (oIter != mapExistingDstDims.end() &&
     863           9 :                         oIter->second->GetSize() == dim->GetSize())
     864             :                     {
     865           8 :                         dstArrayDims.emplace_back(oIter->second);
     866             :                     }
     867             :                     else
     868             :                     {
     869           2 :                         std::string newDimName;
     870           2 :                         if (oIter == mapExistingDstDims.end())
     871             :                         {
     872           1 :                             newDimName = dim->GetName();
     873             :                         }
     874             :                         else
     875             :                         {
     876           1 :                             std::string newDimNamePrefix(srcArray->GetName() +
     877           3 :                                                          '_' + dim->GetName());
     878           1 :                             newDimName = newDimNamePrefix;
     879           1 :                             int nIterCount = 2;
     880           0 :                             while (
     881           1 :                                 cpl::contains(mapExistingDstDims, newDimName))
     882             :                             {
     883           0 :                                 newDimName = newDimNamePrefix +
     884           0 :                                              CPLSPrintf("_%d", nIterCount);
     885           0 :                                 nIterCount++;
     886             :                             }
     887             :                         }
     888           4 :                         dstDim = CreateDimension(newDimName, dim->GetType(),
     889             :                                                  dim->GetDirection(),
     890           4 :                                                  dim->GetSize());
     891           2 :                         if (!dstDim)
     892           0 :                             return false;
     893           2 :                         mapExistingDstDims[newDimName] = dstDim;
     894           2 :                         dstArrayDims.emplace_back(dstDim);
     895             :                     }
     896             :                 }
     897             :             }
     898             : 
     899          78 :             CPLStringList aosArrayCO;
     900          39 :             bool bAutoScale = false;
     901          39 :             GDALDataType eAutoScaleType = GDT_UInt16;
     902          46 :             for (const char *pszItem : cpl::Iterate(papszOptions))
     903             :             {
     904           7 :                 if (STARTS_WITH_CI(pszItem, "ARRAY:"))
     905             :                 {
     906           7 :                     const char *pszOption = pszItem + strlen("ARRAY:");
     907           7 :                     if (STARTS_WITH_CI(pszOption, "IF(DIM="))
     908             :                     {
     909           1 :                         const char *pszNext = strchr(pszOption, ':');
     910           1 :                         if (pszNext != nullptr)
     911             :                         {
     912           1 :                             int nDim = atoi(pszOption + strlen("IF(DIM="));
     913           1 :                             if (static_cast<size_t>(nDim) ==
     914           1 :                                 dstArrayDims.size())
     915             :                             {
     916           1 :                                 pszOption = pszNext + 1;
     917             :                             }
     918             :                             else
     919             :                             {
     920           0 :                                 pszOption = nullptr;
     921             :                             }
     922             :                         }
     923             :                     }
     924           6 :                     else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
     925             :                     {
     926           2 :                         const char *pszName = pszOption + strlen("IF(NAME=");
     927           2 :                         const char *pszNext = strchr(pszName, ':');
     928           2 :                         if (pszNext != nullptr && pszNext > pszName &&
     929           2 :                             pszNext[-1] == ')')
     930             :                         {
     931           4 :                             CPLString osName;
     932           2 :                             osName.assign(pszName, pszNext - pszName - 1);
     933           3 :                             if (osName == srcArray->GetName() ||
     934           1 :                                 osName == srcArray->GetFullName())
     935             :                             {
     936           2 :                                 pszOption = pszNext + 1;
     937             :                             }
     938             :                             else
     939             :                             {
     940           0 :                                 pszOption = nullptr;
     941             :                             }
     942             :                         }
     943             :                     }
     944           7 :                     if (pszOption)
     945             :                     {
     946           7 :                         if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
     947             :                         {
     948             :                             bAutoScale =
     949           2 :                                 CPLTestBool(pszOption + strlen("AUTOSCALE="));
     950             :                         }
     951           5 :                         else if (STARTS_WITH_CI(pszOption,
     952             :                                                 "AUTOSCALE_DATA_TYPE="))
     953             :                         {
     954           1 :                             const char *pszDataType =
     955             :                                 pszOption + strlen("AUTOSCALE_DATA_TYPE=");
     956           1 :                             eAutoScaleType = GDALGetDataTypeByName(pszDataType);
     957           2 :                             if (GDALDataTypeIsComplex(eAutoScaleType) ||
     958           1 :                                 GDALDataTypeIsFloating(eAutoScaleType))
     959             :                             {
     960           0 :                                 CPLError(CE_Failure, CPLE_NotSupported,
     961             :                                          "Unsupported value for "
     962             :                                          "AUTOSCALE_DATA_TYPE");
     963           0 :                                 return false;
     964             :                             }
     965             :                         }
     966             :                         else
     967             :                         {
     968           4 :                             aosArrayCO.AddString(pszOption);
     969             :                         }
     970             :                     }
     971             :                 }
     972             :             }
     973             : 
     974             :             auto oIterDimName =
     975          39 :                 mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
     976          39 :             const auto &srcArrayType = srcArray->GetDataType();
     977             : 
     978          39 :             std::shared_ptr<GDALMDArray> dstArray;
     979             : 
     980             :             // Only autoscale non-indexing variables
     981          39 :             bool bHasOffset = false;
     982          39 :             bool bHasScale = false;
     983           4 :             if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
     984           2 :                 (srcArrayType.GetNumericDataType() == GDT_Float32 ||
     985           0 :                  srcArrayType.GetNumericDataType() == GDT_Float64) &&
     986           2 :                 srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
     987          43 :                 srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
     988          41 :                 oIterDimName == mapSrcVariableNameToIndexedDimName.end())
     989             :             {
     990           2 :                 constexpr bool bApproxOK = false;
     991           2 :                 constexpr bool bForce = true;
     992           2 :                 double dfMin = 0.0;
     993           2 :                 double dfMax = 0.0;
     994           2 :                 if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
     995             :                                             nullptr, nullptr, nullptr, nullptr,
     996           2 :                                             nullptr) != CE_None)
     997             :                 {
     998           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     999             :                              "Could not retrieve statistics for array %s",
    1000           0 :                              srcArray->GetName().c_str());
    1001           0 :                     return false;
    1002             :                 }
    1003           2 :                 double dfDTMin = 0;
    1004           2 :                 double dfDTMax = 0;
    1005             : #define setDTMinMax(ctype)                                                     \
    1006             :     do                                                                         \
    1007             :     {                                                                          \
    1008             :         dfDTMin = static_cast<double>(std::numeric_limits<ctype>::min());      \
    1009             :         dfDTMax = static_cast<double>(std::numeric_limits<ctype>::max());      \
    1010             :     } while (0)
    1011             : 
    1012           2 :                 switch (eAutoScaleType)
    1013             :                 {
    1014           0 :                     case GDT_Byte:
    1015           0 :                         setDTMinMax(GByte);
    1016           0 :                         break;
    1017           0 :                     case GDT_Int8:
    1018           0 :                         setDTMinMax(GInt8);
    1019           0 :                         break;
    1020           1 :                     case GDT_UInt16:
    1021           1 :                         setDTMinMax(GUInt16);
    1022           1 :                         break;
    1023           1 :                     case GDT_Int16:
    1024           1 :                         setDTMinMax(GInt16);
    1025           1 :                         break;
    1026           0 :                     case GDT_UInt32:
    1027           0 :                         setDTMinMax(GUInt32);
    1028           0 :                         break;
    1029           0 :                     case GDT_Int32:
    1030           0 :                         setDTMinMax(GInt32);
    1031           0 :                         break;
    1032           0 :                     case GDT_UInt64:
    1033           0 :                         setDTMinMax(std::uint64_t);
    1034           0 :                         break;
    1035           0 :                     case GDT_Int64:
    1036           0 :                         setDTMinMax(std::int64_t);
    1037           0 :                         break;
    1038           0 :                     case GDT_Float32:
    1039             :                     case GDT_Float64:
    1040             :                     case GDT_Unknown:
    1041             :                     case GDT_CInt16:
    1042             :                     case GDT_CInt32:
    1043             :                     case GDT_CFloat32:
    1044             :                     case GDT_CFloat64:
    1045             :                     case GDT_TypeCount:
    1046           0 :                         CPLAssert(false);
    1047             :                 }
    1048             : 
    1049             :                 dstArray =
    1050           4 :                     CreateMDArray(srcArray->GetName(), dstArrayDims,
    1051           4 :                                   GDALExtendedDataType::Create(eAutoScaleType),
    1052           4 :                                   aosArrayCO.List());
    1053           2 :                 if (!dstArray)
    1054           0 :                     return !bStrict;
    1055             : 
    1056           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1057             :                 {
    1058             :                     // If there's a nodata value in the source array, reserve
    1059             :                     // DTMax for that purpose in the target scaled array
    1060           1 :                     if (!dstArray->SetNoDataValue(dfDTMax))
    1061             :                     {
    1062           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    1063             :                                  "Cannot set nodata value");
    1064           0 :                         return false;
    1065             :                     }
    1066           1 :                     dfDTMax--;
    1067             :                 }
    1068           2 :                 const double dfScale =
    1069           2 :                     dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
    1070           2 :                 const double dfOffset = dfMin - dfDTMin * dfScale;
    1071             : 
    1072           4 :                 if (!dstArray->SetOffset(dfOffset) ||
    1073           2 :                     !dstArray->SetScale(dfScale))
    1074             :                 {
    1075           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1076             :                              "Cannot set scale/offset");
    1077           0 :                     return false;
    1078             :                 }
    1079             : 
    1080           2 :                 auto poUnscaled = dstArray->GetUnscaled();
    1081           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1082             :                 {
    1083           1 :                     poUnscaled->SetNoDataValue(
    1084             :                         srcArray->GetNoDataValueAsDouble());
    1085             :                 }
    1086             : 
    1087             :                 // Copy source array into unscaled array
    1088           4 :                 if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1089             :                                           nCurCost, nTotalCost, pfnProgress,
    1090           2 :                                           pProgressData))
    1091             :                 {
    1092           0 :                     return false;
    1093             :                 }
    1094             :             }
    1095             :             else
    1096             :             {
    1097          74 :                 dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
    1098          74 :                                          srcArrayType, aosArrayCO.List());
    1099          37 :                 if (!dstArray)
    1100           0 :                     return !bStrict;
    1101             : 
    1102          74 :                 if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1103             :                                         nCurCost, nTotalCost, pfnProgress,
    1104          37 :                                         pProgressData))
    1105             :                 {
    1106           0 :                     return false;
    1107             :                 }
    1108             :             }
    1109             : 
    1110             :             // If this array is the indexing variable of a dimension, link them
    1111             :             // together.
    1112          39 :             if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
    1113             :             {
    1114             :                 auto oCorrespondingDimIter =
    1115          16 :                     mapExistingDstDims.find(oIterDimName->second);
    1116          16 :                 if (oCorrespondingDimIter != mapExistingDstDims.end())
    1117             :                 {
    1118             :                     CPLErrorStateBackuper oErrorStateBackuper(
    1119          16 :                         CPLQuietErrorHandler);
    1120          32 :                     oCorrespondingDimIter->second->SetIndexingVariable(
    1121          16 :                         std::move(dstArray));
    1122             :                 }
    1123             :             }
    1124             : 
    1125          39 :             return true;
    1126          22 :         };
    1127             : 
    1128          44 :         const auto arrayNames = poSrcGroup->GetMDArrayNames();
    1129             : 
    1130             :         // Start by copying arrays that are indexing variables of dimensions
    1131          61 :         for (const auto &name : arrayNames)
    1132             :         {
    1133          39 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1134          39 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1135             : 
    1136          39 :             if (cpl::contains(mapSrcVariableNameToIndexedDimName,
    1137          39 :                               srcArray->GetName()))
    1138             :             {
    1139          16 :                 if (!CopyArray(srcArray))
    1140           0 :                     return false;
    1141             :             }
    1142             :         }
    1143             : 
    1144             :         // Then copy regular arrays
    1145          61 :         for (const auto &name : arrayNames)
    1146             :         {
    1147          39 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1148          39 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1149             : 
    1150          39 :             if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
    1151          39 :                                srcArray->GetName()))
    1152             :             {
    1153          23 :                 if (!CopyArray(srcArray))
    1154           0 :                     return false;
    1155             :             }
    1156             :         }
    1157             : 
    1158          44 :         const auto groupNames = poSrcGroup->GetGroupNames();
    1159          26 :         for (const auto &name : groupNames)
    1160             :         {
    1161           4 :             auto srcSubGroup = poSrcGroup->OpenGroup(name);
    1162           4 :             EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
    1163           4 :             auto dstSubGroup = CreateGroup(name);
    1164           4 :             EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
    1165           8 :             if (!dstSubGroup->CopyFrom(
    1166             :                     poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
    1167           4 :                     nTotalCost, pfnProgress, pProgressData, papszOptions))
    1168           0 :                 return false;
    1169             :         }
    1170             : 
    1171          22 :         if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    1172           0 :             return false;
    1173             : 
    1174          22 :         return true;
    1175             :     }
    1176           0 :     catch (const std::exception &e)
    1177             :     {
    1178           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1179           0 :         return false;
    1180             :     }
    1181             : }
    1182             : 
    1183             : /************************************************************************/
    1184             : /*                         GetInnerMostGroup()                          */
    1185             : /************************************************************************/
    1186             : 
    1187             : //! @cond Doxygen_Suppress
    1188             : const GDALGroup *
    1189        1038 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
    1190             :                              std::shared_ptr<GDALGroup> &curGroupHolder,
    1191             :                              std::string &osLastPart) const
    1192             : {
    1193        1038 :     if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
    1194           6 :         return nullptr;
    1195        1032 :     const GDALGroup *poCurGroup = this;
    1196             :     CPLStringList aosTokens(
    1197        2064 :         CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
    1198        1032 :     if (aosTokens.size() == 0)
    1199             :     {
    1200           0 :         return nullptr;
    1201             :     }
    1202             : 
    1203        1367 :     for (int i = 0; i < aosTokens.size() - 1; i++)
    1204             :     {
    1205         342 :         curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
    1206         342 :         if (!curGroupHolder)
    1207             :         {
    1208           7 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
    1209             :                      aosTokens[i]);
    1210           7 :             return nullptr;
    1211             :         }
    1212         335 :         poCurGroup = curGroupHolder.get();
    1213             :     }
    1214        1025 :     osLastPart = aosTokens[aosTokens.size() - 1];
    1215        1025 :     return poCurGroup;
    1216             : }
    1217             : 
    1218             : //! @endcond
    1219             : 
    1220             : /************************************************************************/
    1221             : /*                      OpenMDArrayFromFullname()                       */
    1222             : /************************************************************************/
    1223             : 
    1224             : /** Get an array from its fully qualified name */
    1225             : std::shared_ptr<GDALMDArray>
    1226         352 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
    1227             :                                    CSLConstList papszOptions) const
    1228             : {
    1229         704 :     std::string osName;
    1230         352 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1231         352 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1232         352 :     if (poGroup == nullptr)
    1233           9 :         return nullptr;
    1234         343 :     return poGroup->OpenMDArray(osName, papszOptions);
    1235             : }
    1236             : 
    1237             : /************************************************************************/
    1238             : /*                          ResolveMDArray()                            */
    1239             : /************************************************************************/
    1240             : 
    1241             : /** Locate an array in a group and its subgroups by name.
    1242             :  *
    1243             :  * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
    1244             :  * used
    1245             :  * Otherwise the search will start from the group identified by osStartingPath,
    1246             :  * and an array whose name is osName will be looked for in this group (if
    1247             :  * osStartingPath is empty or "/", then the current group is used). If there
    1248             :  * is no match, then a recursive descendent search will be made in its
    1249             :  * subgroups. If there is no match in the subgroups, then the parent (if
    1250             :  * existing) of the group pointed by osStartingPath will be used as the new
    1251             :  * starting point for the search.
    1252             :  *
    1253             :  * @param osName name, qualified or not
    1254             :  * @param osStartingPath fully qualified name of the (sub-)group from which
    1255             :  *                       the search should be started. If this is a non-empty
    1256             :  *                       string, the group on which this method is called should
    1257             :  *                       nominally be the root group (otherwise the path will
    1258             :  *                       be interpreted as from the current group)
    1259             :  * @param papszOptions options to pass to OpenMDArray()
    1260             :  * @since GDAL 3.2
    1261             :  */
    1262             : std::shared_ptr<GDALMDArray>
    1263          19 : GDALGroup::ResolveMDArray(const std::string &osName,
    1264             :                           const std::string &osStartingPath,
    1265             :                           CSLConstList papszOptions) const
    1266             : {
    1267          19 :     if (!osName.empty() && osName[0] == '/')
    1268             :     {
    1269           1 :         auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
    1270           1 :         if (poArray)
    1271           1 :             return poArray;
    1272             :     }
    1273          36 :     std::string osPath(osStartingPath);
    1274          36 :     std::set<std::string> oSetAlreadyVisited;
    1275             : 
    1276             :     while (true)
    1277             :     {
    1278           0 :         std::shared_ptr<GDALGroup> curGroupHolder;
    1279           0 :         std::shared_ptr<GDALGroup> poGroup;
    1280             : 
    1281          22 :         std::queue<std::shared_ptr<GDALGroup>> oQueue;
    1282          22 :         bool goOn = false;
    1283          22 :         if (osPath.empty() || osPath == "/")
    1284             :         {
    1285          11 :             goOn = true;
    1286             :         }
    1287             :         else
    1288             :         {
    1289          22 :             std::string osLastPart;
    1290             :             const GDALGroup *poGroupPtr =
    1291          11 :                 GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
    1292          11 :             if (poGroupPtr)
    1293          11 :                 poGroup = poGroupPtr->OpenGroup(osLastPart);
    1294          22 :             if (poGroup &&
    1295          22 :                 !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
    1296             :             {
    1297          11 :                 oQueue.push(poGroup);
    1298          11 :                 goOn = true;
    1299             :             }
    1300             :         }
    1301             : 
    1302          22 :         if (goOn)
    1303             :         {
    1304          17 :             do
    1305             :             {
    1306             :                 const GDALGroup *groupPtr;
    1307          39 :                 if (!oQueue.empty())
    1308             :                 {
    1309          28 :                     poGroup = oQueue.front();
    1310          28 :                     oQueue.pop();
    1311          28 :                     groupPtr = poGroup.get();
    1312             :                 }
    1313             :                 else
    1314             :                 {
    1315          11 :                     groupPtr = this;
    1316             :                 }
    1317             : 
    1318          39 :                 auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
    1319          39 :                 if (poArray)
    1320          16 :                     return poArray;
    1321             : 
    1322          46 :                 const auto aosGroupNames = groupPtr->GetGroupNames();
    1323          47 :                 for (const auto &osGroupName : aosGroupNames)
    1324             :                 {
    1325          48 :                     auto poSubGroup = groupPtr->OpenGroup(osGroupName);
    1326          48 :                     if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
    1327          48 :                                                      poSubGroup->GetFullName()))
    1328             :                     {
    1329          24 :                         oQueue.push(poSubGroup);
    1330          24 :                         oSetAlreadyVisited.insert(poSubGroup->GetFullName());
    1331             :                     }
    1332             :                 }
    1333          23 :             } while (!oQueue.empty());
    1334             :         }
    1335             : 
    1336           6 :         if (osPath.empty() || osPath == "/")
    1337           2 :             break;
    1338             : 
    1339           4 :         const auto nPos = osPath.rfind('/');
    1340           4 :         if (nPos == 0)
    1341           1 :             osPath = "/";
    1342             :         else
    1343             :         {
    1344           3 :             if (nPos == std::string::npos)
    1345           0 :                 break;
    1346           3 :             osPath.resize(nPos);
    1347             :         }
    1348           4 :     }
    1349           2 :     return nullptr;
    1350             : }
    1351             : 
    1352             : /************************************************************************/
    1353             : /*                       OpenGroupFromFullname()                        */
    1354             : /************************************************************************/
    1355             : 
    1356             : /** Get a group from its fully qualified name.
    1357             :  * @since GDAL 3.2
    1358             :  */
    1359             : std::shared_ptr<GDALGroup>
    1360         550 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
    1361             :                                  CSLConstList papszOptions) const
    1362             : {
    1363        1100 :     std::string osName;
    1364         550 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1365         550 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1366         550 :     if (poGroup == nullptr)
    1367           2 :         return nullptr;
    1368         548 :     return poGroup->OpenGroup(osName, papszOptions);
    1369             : }
    1370             : 
    1371             : /************************************************************************/
    1372             : /*                      OpenDimensionFromFullname()                     */
    1373             : /************************************************************************/
    1374             : 
    1375             : /** Get a dimension from its fully qualified name */
    1376             : std::shared_ptr<GDALDimension>
    1377         125 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
    1378             : {
    1379         250 :     std::string osName;
    1380         125 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1381         125 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1382         125 :     if (poGroup == nullptr)
    1383           2 :         return nullptr;
    1384         246 :     auto dims(poGroup->GetDimensions());
    1385         203 :     for (auto &dim : dims)
    1386             :     {
    1387         164 :         if (dim->GetName() == osName)
    1388          84 :             return dim;
    1389             :     }
    1390          39 :     return nullptr;
    1391             : }
    1392             : 
    1393             : /************************************************************************/
    1394             : /*                           ClearStatistics()                          */
    1395             : /************************************************************************/
    1396             : 
    1397             : /**
    1398             :  * \brief Clear statistics.
    1399             :  *
    1400             :  * @since GDAL 3.4
    1401             :  */
    1402           0 : void GDALGroup::ClearStatistics()
    1403             : {
    1404           0 :     auto groupNames = GetGroupNames();
    1405           0 :     for (const auto &name : groupNames)
    1406             :     {
    1407           0 :         auto subGroup = OpenGroup(name);
    1408           0 :         if (subGroup)
    1409             :         {
    1410           0 :             subGroup->ClearStatistics();
    1411             :         }
    1412             :     }
    1413             : 
    1414           0 :     auto arrayNames = GetMDArrayNames();
    1415           0 :     for (const auto &name : arrayNames)
    1416             :     {
    1417           0 :         auto array = OpenMDArray(name);
    1418           0 :         if (array)
    1419             :         {
    1420           0 :             array->ClearStatistics();
    1421             :         }
    1422             :     }
    1423           0 : }
    1424             : 
    1425             : /************************************************************************/
    1426             : /*                            Rename()                                  */
    1427             : /************************************************************************/
    1428             : 
    1429             : /** Rename the group.
    1430             :  *
    1431             :  * This is not implemented by all drivers.
    1432             :  *
    1433             :  * Drivers known to implement it: MEM, netCDF, ZARR.
    1434             :  *
    1435             :  * This is the same as the C function GDALGroupRename().
    1436             :  *
    1437             :  * @param osNewName New name.
    1438             :  *
    1439             :  * @return true in case of success
    1440             :  * @since GDAL 3.8
    1441             :  */
    1442           0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
    1443             : {
    1444           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
    1445           0 :     return false;
    1446             : }
    1447             : 
    1448             : /************************************************************************/
    1449             : /*                         BaseRename()                                 */
    1450             : /************************************************************************/
    1451             : 
    1452             : //! @cond Doxygen_Suppress
    1453           8 : void GDALGroup::BaseRename(const std::string &osNewName)
    1454             : {
    1455           8 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
    1456           8 :     m_osFullName += osNewName;
    1457           8 :     m_osName = osNewName;
    1458             : 
    1459           8 :     NotifyChildrenOfRenaming();
    1460           8 : }
    1461             : 
    1462             : //! @endcond
    1463             : 
    1464             : /************************************************************************/
    1465             : /*                        ParentRenamed()                               */
    1466             : /************************************************************************/
    1467             : 
    1468             : //! @cond Doxygen_Suppress
    1469           7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
    1470             : {
    1471           7 :     m_osFullName = osNewParentFullName;
    1472           7 :     m_osFullName += "/";
    1473           7 :     m_osFullName += m_osName;
    1474             : 
    1475           7 :     NotifyChildrenOfRenaming();
    1476           7 : }
    1477             : 
    1478             : //! @endcond
    1479             : 
    1480             : /************************************************************************/
    1481             : /*                             Deleted()                                */
    1482             : /************************************************************************/
    1483             : 
    1484             : //! @cond Doxygen_Suppress
    1485          22 : void GDALGroup::Deleted()
    1486             : {
    1487          22 :     m_bValid = false;
    1488             : 
    1489          22 :     NotifyChildrenOfDeletion();
    1490          22 : }
    1491             : 
    1492             : //! @endcond
    1493             : 
    1494             : /************************************************************************/
    1495             : /*                        ParentDeleted()                               */
    1496             : /************************************************************************/
    1497             : 
    1498             : //! @cond Doxygen_Suppress
    1499           3 : void GDALGroup::ParentDeleted()
    1500             : {
    1501           3 :     Deleted();
    1502           3 : }
    1503             : 
    1504             : //! @endcond
    1505             : 
    1506             : /************************************************************************/
    1507             : /*                     CheckValidAndErrorOutIfNot()                     */
    1508             : /************************************************************************/
    1509             : 
    1510             : //! @cond Doxygen_Suppress
    1511       11755 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
    1512             : {
    1513       11755 :     if (!m_bValid)
    1514             :     {
    1515          14 :         CPLError(CE_Failure, CPLE_AppDefined,
    1516             :                  "This object has been deleted. No action on it is possible");
    1517             :     }
    1518       11755 :     return m_bValid;
    1519             : }
    1520             : 
    1521             : //! @endcond
    1522             : 
    1523             : /************************************************************************/
    1524             : /*                       ~GDALAbstractMDArray()                         */
    1525             : /************************************************************************/
    1526             : 
    1527             : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
    1528             : 
    1529             : /************************************************************************/
    1530             : /*                        GDALAbstractMDArray()                         */
    1531             : /************************************************************************/
    1532             : 
    1533             : //! @cond Doxygen_Suppress
    1534       20196 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
    1535       20196 :                                          const std::string &osName)
    1536             :     : m_osName(osName),
    1537             :       m_osFullName(
    1538       20196 :           !osParentName.empty()
    1539       38754 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
    1540       79146 :               : osName)
    1541             : {
    1542       20196 : }
    1543             : 
    1544             : //! @endcond
    1545             : 
    1546             : /************************************************************************/
    1547             : /*                           GetDimensions()                            */
    1548             : /************************************************************************/
    1549             : 
    1550             : /** \fn GDALAbstractMDArray::GetDimensions() const
    1551             :  * \brief Return the dimensions of an attribute/array.
    1552             :  *
    1553             :  * This is the same as the C functions GDALMDArrayGetDimensions() and
    1554             :  * similar to GDALAttributeGetDimensionsSize().
    1555             :  */
    1556             : 
    1557             : /************************************************************************/
    1558             : /*                           GetDataType()                              */
    1559             : /************************************************************************/
    1560             : 
    1561             : /** \fn GDALAbstractMDArray::GetDataType() const
    1562             :  * \brief Return the data type of an attribute/array.
    1563             :  *
    1564             :  * This is the same as the C functions GDALMDArrayGetDataType() and
    1565             :  * GDALAttributeGetDataType()
    1566             :  */
    1567             : 
    1568             : /************************************************************************/
    1569             : /*                        GetDimensionCount()                           */
    1570             : /************************************************************************/
    1571             : 
    1572             : /** Return the number of dimensions.
    1573             :  *
    1574             :  * Default implementation is GetDimensions().size(), and may be overridden by
    1575             :  * drivers if they have a faster / less expensive implementations.
    1576             :  *
    1577             :  * This is the same as the C function GDALMDArrayGetDimensionCount() or
    1578             :  * GDALAttributeGetDimensionCount().
    1579             :  *
    1580             :  */
    1581       22267 : size_t GDALAbstractMDArray::GetDimensionCount() const
    1582             : {
    1583       22267 :     return GetDimensions().size();
    1584             : }
    1585             : 
    1586             : /************************************************************************/
    1587             : /*                            Rename()                                  */
    1588             : /************************************************************************/
    1589             : 
    1590             : /** Rename the attribute/array.
    1591             :  *
    1592             :  * This is not implemented by all drivers.
    1593             :  *
    1594             :  * Drivers known to implement it: MEM, netCDF, Zarr.
    1595             :  *
    1596             :  * This is the same as the C functions GDALMDArrayRename() or
    1597             :  * GDALAttributeRename().
    1598             :  *
    1599             :  * @param osNewName New name.
    1600             :  *
    1601             :  * @return true in case of success
    1602             :  * @since GDAL 3.8
    1603             :  */
    1604           0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
    1605             : {
    1606           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
    1607           0 :     return false;
    1608             : }
    1609             : 
    1610             : /************************************************************************/
    1611             : /*                             CopyValue()                              */
    1612             : /************************************************************************/
    1613             : 
    1614             : /** Convert a value from a source type to a destination type.
    1615             :  *
    1616             :  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
    1617             :  * that must be freed with CPLFree().
    1618             :  */
    1619       78422 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
    1620             :                                      const GDALExtendedDataType &srcType,
    1621             :                                      void *pDst,
    1622             :                                      const GDALExtendedDataType &dstType)
    1623             : {
    1624      153554 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1625       75132 :         dstType.GetClass() == GEDTC_NUMERIC)
    1626             :     {
    1627       74925 :         GDALCopyWords(pSrc, srcType.GetNumericDataType(), 0, pDst,
    1628             :                       dstType.GetNumericDataType(), 0, 1);
    1629       74925 :         return true;
    1630             :     }
    1631        6612 :     if (srcType.GetClass() == GEDTC_STRING &&
    1632        3115 :         dstType.GetClass() == GEDTC_STRING)
    1633             :     {
    1634             :         const char *srcStrPtr;
    1635        2731 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
    1636        2731 :         char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
    1637        2731 :         *reinterpret_cast<void **>(pDst) = pszDup;
    1638        2731 :         return true;
    1639             :     }
    1640         973 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1641         207 :         dstType.GetClass() == GEDTC_STRING)
    1642             :     {
    1643         207 :         const char *str = nullptr;
    1644         207 :         switch (srcType.GetNumericDataType())
    1645             :         {
    1646           0 :             case GDT_Unknown:
    1647           0 :                 break;
    1648           0 :             case GDT_Byte:
    1649           0 :                 str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
    1650           0 :                 break;
    1651           3 :             case GDT_Int8:
    1652           3 :                 str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
    1653           3 :                 break;
    1654          48 :             case GDT_UInt16:
    1655          48 :                 str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
    1656          48 :                 break;
    1657           0 :             case GDT_Int16:
    1658           0 :                 str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
    1659           0 :                 break;
    1660           8 :             case GDT_UInt32:
    1661           8 :                 str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
    1662           8 :                 break;
    1663          54 :             case GDT_Int32:
    1664          54 :                 str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
    1665          54 :                 break;
    1666           0 :             case GDT_UInt64:
    1667             :                 str =
    1668           0 :                     CPLSPrintf(CPL_FRMT_GUIB,
    1669             :                                static_cast<GUIntBig>(
    1670             :                                    *static_cast<const std::uint64_t *>(pSrc)));
    1671           0 :                 break;
    1672           0 :             case GDT_Int64:
    1673           0 :                 str = CPLSPrintf(CPL_FRMT_GIB,
    1674             :                                  static_cast<GIntBig>(
    1675             :                                      *static_cast<const std::int64_t *>(pSrc)));
    1676           0 :                 break;
    1677          17 :             case GDT_Float32:
    1678          17 :                 str = CPLSPrintf("%.9g", *static_cast<const float *>(pSrc));
    1679          17 :                 break;
    1680          75 :             case GDT_Float64:
    1681          75 :                 str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
    1682          75 :                 break;
    1683           2 :             case GDT_CInt16:
    1684             :             {
    1685           2 :                 const GInt16 *src = static_cast<const GInt16 *>(pSrc);
    1686           2 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
    1687           2 :                 break;
    1688             :             }
    1689           0 :             case GDT_CInt32:
    1690             :             {
    1691           0 :                 const GInt32 *src = static_cast<const GInt32 *>(pSrc);
    1692           0 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
    1693           0 :                 break;
    1694             :             }
    1695           0 :             case GDT_CFloat32:
    1696             :             {
    1697           0 :                 const float *src = static_cast<const float *>(pSrc);
    1698           0 :                 str = CPLSPrintf("%.9g+%.9gj", src[0], src[1]);
    1699           0 :                 break;
    1700             :             }
    1701           0 :             case GDT_CFloat64:
    1702             :             {
    1703           0 :                 const double *src = static_cast<const double *>(pSrc);
    1704           0 :                 str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
    1705           0 :                 break;
    1706             :             }
    1707           0 :             case GDT_TypeCount:
    1708           0 :                 CPLAssert(false);
    1709             :                 break;
    1710             :         }
    1711         207 :         char *pszDup = str ? CPLStrdup(str) : nullptr;
    1712         207 :         *reinterpret_cast<void **>(pDst) = pszDup;
    1713         207 :         return true;
    1714             :     }
    1715         943 :     if (srcType.GetClass() == GEDTC_STRING &&
    1716         384 :         dstType.GetClass() == GEDTC_NUMERIC)
    1717             :     {
    1718             :         const char *srcStrPtr;
    1719         384 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
    1720         384 :         if (dstType.GetNumericDataType() == GDT_Int64)
    1721             :         {
    1722           2 :             *(static_cast<int64_t *>(pDst)) =
    1723           2 :                 srcStrPtr == nullptr ? 0
    1724           1 :                                      : static_cast<int64_t>(atoll(srcStrPtr));
    1725             :         }
    1726         382 :         else if (dstType.GetNumericDataType() == GDT_UInt64)
    1727             :         {
    1728           2 :             *(static_cast<uint64_t *>(pDst)) =
    1729           2 :                 srcStrPtr == nullptr
    1730           2 :                     ? 0
    1731           1 :                     : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
    1732             :         }
    1733             :         else
    1734             :         {
    1735             :             // FIXME GDT_UInt64
    1736         380 :             const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
    1737         380 :             GDALCopyWords(&dfVal, GDT_Float64, 0, pDst,
    1738             :                           dstType.GetNumericDataType(), 0, 1);
    1739             :         }
    1740         384 :         return true;
    1741             :     }
    1742         350 :     if (srcType.GetClass() == GEDTC_COMPOUND &&
    1743         175 :         dstType.GetClass() == GEDTC_COMPOUND)
    1744             :     {
    1745         175 :         const auto &srcComponents = srcType.GetComponents();
    1746         175 :         const auto &dstComponents = dstType.GetComponents();
    1747         175 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
    1748         175 :         GByte *pabyDst = static_cast<GByte *>(pDst);
    1749             : 
    1750             :         std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
    1751         350 :             srcComponentMap;
    1752         688 :         for (const auto &srcComp : srcComponents)
    1753             :         {
    1754         513 :             srcComponentMap[srcComp->GetName()] = &srcComp;
    1755             :         }
    1756         504 :         for (const auto &dstComp : dstComponents)
    1757             :         {
    1758         329 :             auto oIter = srcComponentMap.find(dstComp->GetName());
    1759         329 :             if (oIter == srcComponentMap.end())
    1760           0 :                 return false;
    1761         329 :             const auto &srcComp = *(oIter->second);
    1762         987 :             if (!GDALExtendedDataType::CopyValue(
    1763         329 :                     pabySrc + srcComp->GetOffset(), srcComp->GetType(),
    1764         329 :                     pabyDst + dstComp->GetOffset(), dstComp->GetType()))
    1765             :             {
    1766           0 :                 return false;
    1767             :             }
    1768             :         }
    1769         175 :         return true;
    1770             :     }
    1771             : 
    1772           0 :     return false;
    1773             : }
    1774             : 
    1775             : /************************************************************************/
    1776             : /*                             CopyValues()                             */
    1777             : /************************************************************************/
    1778             : 
    1779             : /** Convert severals value from a source type to a destination type.
    1780             :  *
    1781             :  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
    1782             :  * that must be freed with CPLFree().
    1783             :  */
    1784         328 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
    1785             :                                       const GDALExtendedDataType &srcType,
    1786             :                                       GPtrDiff_t nSrcStrideInElts, void *pDst,
    1787             :                                       const GDALExtendedDataType &dstType,
    1788             :                                       GPtrDiff_t nDstStrideInElts,
    1789             :                                       size_t nValues)
    1790             : {
    1791             :     const auto nSrcStrideInBytes =
    1792         328 :         nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
    1793             :     const auto nDstStrideInBytes =
    1794         328 :         nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
    1795         594 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1796         266 :         dstType.GetClass() == GEDTC_NUMERIC &&
    1797         266 :         nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
    1798         266 :         nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
    1799         860 :         nDstStrideInBytes >= std::numeric_limits<int>::min() &&
    1800         266 :         nDstStrideInBytes <= std::numeric_limits<int>::max())
    1801             :     {
    1802         266 :         GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
    1803             :                         static_cast<int>(nSrcStrideInBytes), pDst,
    1804             :                         dstType.GetNumericDataType(),
    1805             :                         static_cast<int>(nDstStrideInBytes), nValues);
    1806             :     }
    1807             :     else
    1808             :     {
    1809          62 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
    1810          62 :         GByte *pabyDst = static_cast<GByte *>(pDst);
    1811         124 :         for (size_t i = 0; i < nValues; ++i)
    1812             :         {
    1813          62 :             if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
    1814           0 :                 return false;
    1815          62 :             pabySrc += nSrcStrideInBytes;
    1816          62 :             pabyDst += nDstStrideInBytes;
    1817             :         }
    1818             :     }
    1819         328 :     return true;
    1820             : }
    1821             : 
    1822             : /************************************************************************/
    1823             : /*                       CheckReadWriteParams()                         */
    1824             : /************************************************************************/
    1825             : //! @cond Doxygen_Suppress
    1826        7823 : bool GDALAbstractMDArray::CheckReadWriteParams(
    1827             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
    1828             :     const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
    1829             :     const void *buffer, const void *buffer_alloc_start,
    1830             :     size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
    1831             :     std::vector<GPtrDiff_t> &tmp_bufferStride) const
    1832             : {
    1833           0 :     const auto lamda_error = []()
    1834             :     {
    1835           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1836             :                  "Not all elements pointed by buffer will fit in "
    1837             :                  "[buffer_alloc_start, "
    1838             :                  "buffer_alloc_start + buffer_alloc_size]");
    1839           0 :     };
    1840             : 
    1841        7823 :     const auto &dims = GetDimensions();
    1842        7823 :     if (dims.empty())
    1843             :     {
    1844        3032 :         if (buffer_alloc_start)
    1845             :         {
    1846        2665 :             const size_t elementSize = bufferDataType.GetSize();
    1847        2665 :             const GByte *paby_buffer = static_cast<const GByte *>(buffer);
    1848        2665 :             const GByte *paby_buffer_alloc_start =
    1849             :                 static_cast<const GByte *>(buffer_alloc_start);
    1850        2665 :             const GByte *paby_buffer_alloc_end =
    1851             :                 paby_buffer_alloc_start + buffer_alloc_size;
    1852             : 
    1853        2665 :             if (paby_buffer < paby_buffer_alloc_start ||
    1854        2665 :                 paby_buffer + elementSize > paby_buffer_alloc_end)
    1855             :             {
    1856           0 :                 lamda_error();
    1857           0 :                 return false;
    1858             :             }
    1859             :         }
    1860        3032 :         return true;
    1861             :     }
    1862             : 
    1863        4791 :     if (arrayStep == nullptr)
    1864             :     {
    1865        1265 :         tmp_arrayStep.resize(dims.size(), 1);
    1866        1265 :         arrayStep = tmp_arrayStep.data();
    1867             :     }
    1868       13508 :     for (size_t i = 0; i < dims.size(); i++)
    1869             :     {
    1870        8717 :         if (count[i] == 0)
    1871             :         {
    1872           0 :             CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
    1873             :                      static_cast<unsigned>(i));
    1874           0 :             return false;
    1875             :         }
    1876             :     }
    1877        4791 :     bool bufferStride_all_positive = true;
    1878        4791 :     if (bufferStride == nullptr)
    1879             :     {
    1880         979 :         GPtrDiff_t stride = 1;
    1881             :         // To compute strides we must proceed from the fastest varying dimension
    1882             :         // (the last one), and then reverse the result
    1883        2229 :         for (size_t i = dims.size(); i != 0;)
    1884             :         {
    1885        1250 :             --i;
    1886        1250 :             tmp_bufferStride.push_back(stride);
    1887        1250 :             GUInt64 newStride = 0;
    1888             :             bool bOK;
    1889             :             try
    1890             :             {
    1891        1250 :                 newStride = (CPLSM(static_cast<GUInt64>(stride)) *
    1892        2500 :                              CPLSM(static_cast<GUInt64>(count[i])))
    1893        1250 :                                 .v();
    1894        1250 :                 bOK = static_cast<size_t>(newStride) == newStride &&
    1895        1250 :                       newStride < std::numeric_limits<size_t>::max() / 2;
    1896             :             }
    1897           0 :             catch (...)
    1898             :             {
    1899           0 :                 bOK = false;
    1900             :             }
    1901        1250 :             if (!bOK)
    1902             :             {
    1903           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
    1904           0 :                 return false;
    1905             :             }
    1906        1250 :             stride = static_cast<GPtrDiff_t>(newStride);
    1907             :         }
    1908         979 :         std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
    1909         979 :         bufferStride = tmp_bufferStride.data();
    1910             :     }
    1911             :     else
    1912             :     {
    1913       11277 :         for (size_t i = 0; i < dims.size(); i++)
    1914             :         {
    1915        7466 :             if (bufferStride[i] < 0)
    1916             :             {
    1917           1 :                 bufferStride_all_positive = false;
    1918           1 :                 break;
    1919             :             }
    1920             :         }
    1921             :     }
    1922       13479 :     for (size_t i = 0; i < dims.size(); i++)
    1923             :     {
    1924        8698 :         if (arrayStartIdx[i] >= dims[i]->GetSize())
    1925             :         {
    1926           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    1927             :                      "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
    1928             :                      static_cast<unsigned>(i),
    1929           2 :                      static_cast<GUInt64>(arrayStartIdx[i]),
    1930           2 :                      static_cast<GUInt64>(dims[i]->GetSize()));
    1931           2 :             return false;
    1932             :         }
    1933             :         bool bOverflow;
    1934        8696 :         if (arrayStep[i] >= 0)
    1935             :         {
    1936             :             try
    1937             :             {
    1938        8102 :                 bOverflow = (CPLSM(static_cast<GUInt64>(arrayStartIdx[i])) +
    1939        8104 :                              CPLSM(static_cast<GUInt64>(count[i] - 1)) *
    1940       32411 :                                  CPLSM(static_cast<GUInt64>(arrayStep[i])))
    1941        8102 :                                 .v() >= dims[i]->GetSize();
    1942             :             }
    1943           1 :             catch (...)
    1944             :             {
    1945           1 :                 bOverflow = true;
    1946             :             }
    1947        8103 :             if (bOverflow)
    1948             :             {
    1949           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1950             :                          "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
    1951             :                          ">= " CPL_FRMT_GUIB,
    1952             :                          static_cast<unsigned>(i), static_cast<unsigned>(i),
    1953             :                          static_cast<unsigned>(i),
    1954           5 :                          static_cast<GUInt64>(dims[i]->GetSize()));
    1955           5 :                 return false;
    1956             :             }
    1957             :         }
    1958             :         else
    1959             :         {
    1960             :             try
    1961             :             {
    1962         593 :                 bOverflow =
    1963         593 :                     arrayStartIdx[i] <
    1964         593 :                     (CPLSM(static_cast<GUInt64>(count[i] - 1)) *
    1965        1186 :                      CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
    1966             :                                ? (static_cast<GUInt64>(1) << 63)
    1967        1186 :                                : static_cast<GUInt64>(-arrayStep[i])))
    1968         593 :                         .v();
    1969             :             }
    1970           0 :             catch (...)
    1971             :             {
    1972           0 :                 bOverflow = true;
    1973             :             }
    1974         593 :             if (bOverflow)
    1975             :             {
    1976           3 :                 CPLError(
    1977             :                     CE_Failure, CPLE_AppDefined,
    1978             :                     "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
    1979             :                     static_cast<unsigned>(i), static_cast<unsigned>(i),
    1980             :                     static_cast<unsigned>(i));
    1981           3 :                 return false;
    1982             :             }
    1983             :         }
    1984             :     }
    1985             : 
    1986        4781 :     if (buffer_alloc_start)
    1987             :     {
    1988        2485 :         const size_t elementSize = bufferDataType.GetSize();
    1989        2485 :         const GByte *paby_buffer = static_cast<const GByte *>(buffer);
    1990        2485 :         const GByte *paby_buffer_alloc_start =
    1991             :             static_cast<const GByte *>(buffer_alloc_start);
    1992        2485 :         const GByte *paby_buffer_alloc_end =
    1993             :             paby_buffer_alloc_start + buffer_alloc_size;
    1994        2485 :         if (bufferStride_all_positive)
    1995             :         {
    1996        2485 :             if (paby_buffer < paby_buffer_alloc_start)
    1997             :             {
    1998           0 :                 lamda_error();
    1999           0 :                 return false;
    2000             :             }
    2001        2485 :             GUInt64 nOffset = elementSize;
    2002        7117 :             for (size_t i = 0; i < dims.size(); i++)
    2003             :             {
    2004             :                 try
    2005             :                 {
    2006        4632 :                     nOffset = (CPLSM(static_cast<GUInt64>(nOffset)) +
    2007        4632 :                                CPLSM(static_cast<GUInt64>(bufferStride[i])) *
    2008        9264 :                                    CPLSM(static_cast<GUInt64>(count[i] - 1)) *
    2009       18528 :                                    CPLSM(static_cast<GUInt64>(elementSize)))
    2010        4632 :                                   .v();
    2011             :                 }
    2012           0 :                 catch (...)
    2013             :                 {
    2014           0 :                     lamda_error();
    2015           0 :                     return false;
    2016             :                 }
    2017             :             }
    2018             : #if SIZEOF_VOIDP == 4
    2019             :             if (static_cast<size_t>(nOffset) != nOffset)
    2020             :             {
    2021             :                 lamda_error();
    2022             :                 return false;
    2023             :             }
    2024             : #endif
    2025        2485 :             if (paby_buffer + nOffset > paby_buffer_alloc_end)
    2026             :             {
    2027           0 :                 lamda_error();
    2028           0 :                 return false;
    2029             :             }
    2030             :         }
    2031           0 :         else if (dims.size() < 31)
    2032             :         {
    2033             :             // Check all corners of the hypercube
    2034           0 :             const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
    2035           0 :             for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
    2036             :             {
    2037           0 :                 const GByte *paby = paby_buffer;
    2038           0 :                 for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
    2039             :                      i++)
    2040             :                 {
    2041           0 :                     if (iCornerCode & (1U << i))
    2042             :                     {
    2043             :                         // We should check for integer overflows
    2044           0 :                         paby += bufferStride[i] * (count[i] - 1) * elementSize;
    2045             :                     }
    2046             :                 }
    2047           0 :                 if (paby < paby_buffer_alloc_start ||
    2048           0 :                     paby + elementSize > paby_buffer_alloc_end)
    2049             :                 {
    2050           0 :                     lamda_error();
    2051           0 :                     return false;
    2052             :                 }
    2053             :             }
    2054             :         }
    2055             :     }
    2056             : 
    2057        4781 :     return true;
    2058             : }
    2059             : 
    2060             : //! @endcond
    2061             : 
    2062             : /************************************************************************/
    2063             : /*                               Read()                                 */
    2064             : /************************************************************************/
    2065             : 
    2066             : /** Read part or totality of a multidimensional array or attribute.
    2067             :  *
    2068             :  * This will extract the content of a hyper-rectangle from the array into
    2069             :  * a user supplied buffer.
    2070             :  *
    2071             :  * If bufferDataType is of type string, the values written in pDstBuffer
    2072             :  * will be char* pointers and the strings should be freed with CPLFree().
    2073             :  *
    2074             :  * This is the same as the C function GDALMDArrayRead().
    2075             :  *
    2076             :  * @param arrayStartIdx Values representing the starting index to read
    2077             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2078             :  *                      Array of GetDimensionCount() values. Must not be
    2079             :  *                      nullptr, unless for a zero-dimensional array.
    2080             :  *
    2081             :  * @param count         Values representing the number of values to extract in
    2082             :  *                      each dimension.
    2083             :  *                      Array of GetDimensionCount() values. Must not be
    2084             :  *                      nullptr, unless for a zero-dimensional array.
    2085             :  *
    2086             :  * @param arrayStep     Spacing between values to extract in each dimension.
    2087             :  *                      The spacing is in number of array elements, not bytes.
    2088             :  *                      If provided, must contain GetDimensionCount() values.
    2089             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
    2090             :  * default to indicate consecutive elements.
    2091             :  *
    2092             :  * @param bufferStride  Spacing between values to store in pDstBuffer.
    2093             :  *                      The spacing is in number of array elements, not bytes.
    2094             :  *                      If provided, must contain GetDimensionCount() values.
    2095             :  *                      Negative values are possible (for example to reorder
    2096             :  *                      from bottom-to-top to top-to-bottom).
    2097             :  *                      If set to nullptr, will be set so that pDstBuffer is
    2098             :  *                      written in a compact way, with elements of the last /
    2099             :  *                      fastest varying dimension being consecutive.
    2100             :  *
    2101             :  * @param bufferDataType Data type of values in pDstBuffer.
    2102             :  *
    2103             :  * @param pDstBuffer    User buffer to store the values read. Should be big
    2104             :  *                      enough to store the number of values indicated by
    2105             :  * count[] and with the spacing of bufferStride[].
    2106             :  *
    2107             :  * @param pDstBufferAllocStart Optional pointer that can be used to validate the
    2108             :  *                             validity of pDstBuffer. pDstBufferAllocStart
    2109             :  * should be the pointer returned by the malloc() or equivalent call used to
    2110             :  * allocate the buffer. It will generally be equal to pDstBuffer (when
    2111             :  * bufferStride[] values are all positive), but not necessarily. If specified,
    2112             :  * nDstBufferAllocSize should be also set to the appropriate value. If no
    2113             :  * validation is needed, nullptr can be passed.
    2114             :  *
    2115             :  * @param nDstBufferAllocSize  Optional buffer size, that can be used to
    2116             :  * validate the validity of pDstBuffer. This is the size of the buffer starting
    2117             :  * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
    2118             :  *                             set to the appropriate value.
    2119             :  *                             If no validation is needed, 0 can be passed.
    2120             :  *
    2121             :  * @return true in case of success.
    2122             :  */
    2123        2343 : bool GDALAbstractMDArray::Read(
    2124             :     const GUInt64 *arrayStartIdx, const size_t *count,
    2125             :     const GInt64 *arrayStep,         // step in elements
    2126             :     const GPtrDiff_t *bufferStride,  // stride in elements
    2127             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    2128             :     const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
    2129             : {
    2130        2343 :     if (!GetDataType().CanConvertTo(bufferDataType))
    2131             :     {
    2132           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2133             :                  "Array data type is not convertible to buffer data type");
    2134           0 :         return false;
    2135             :     }
    2136             : 
    2137        4686 :     std::vector<GInt64> tmp_arrayStep;
    2138        4686 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    2139        2343 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    2140             :                               bufferDataType, pDstBuffer, pDstBufferAllocStart,
    2141             :                               nDstBufferAllocSize, tmp_arrayStep,
    2142             :                               tmp_bufferStride))
    2143             :     {
    2144           0 :         return false;
    2145             :     }
    2146             : 
    2147        2343 :     return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
    2148        2343 :                  pDstBuffer);
    2149             : }
    2150             : 
    2151             : /************************************************************************/
    2152             : /*                                IWrite()                              */
    2153             : /************************************************************************/
    2154             : 
    2155             : //! @cond Doxygen_Suppress
    2156           1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
    2157             :                                  const GInt64 *, const GPtrDiff_t *,
    2158             :                                  const GDALExtendedDataType &, const void *)
    2159             : {
    2160           1 :     CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
    2161           1 :     return false;
    2162             : }
    2163             : 
    2164             : //! @endcond
    2165             : 
    2166             : /************************************************************************/
    2167             : /*                               Write()                                 */
    2168             : /************************************************************************/
    2169             : 
    2170             : /** Write part or totality of a multidimensional array or attribute.
    2171             :  *
    2172             :  * This will set the content of a hyper-rectangle into the array from
    2173             :  * a user supplied buffer.
    2174             :  *
    2175             :  * If bufferDataType is of type string, the values read from pSrcBuffer
    2176             :  * will be char* pointers.
    2177             :  *
    2178             :  * This is the same as the C function GDALMDArrayWrite().
    2179             :  *
    2180             :  * @param arrayStartIdx Values representing the starting index to write
    2181             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2182             :  *                      Array of GetDimensionCount() values. Must not be
    2183             :  *                      nullptr, unless for a zero-dimensional array.
    2184             :  *
    2185             :  * @param count         Values representing the number of values to write in
    2186             :  *                      each dimension.
    2187             :  *                      Array of GetDimensionCount() values. Must not be
    2188             :  *                      nullptr, unless for a zero-dimensional array.
    2189             :  *
    2190             :  * @param arrayStep     Spacing between values to write in each dimension.
    2191             :  *                      The spacing is in number of array elements, not bytes.
    2192             :  *                      If provided, must contain GetDimensionCount() values.
    2193             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
    2194             :  * default to indicate consecutive elements.
    2195             :  *
    2196             :  * @param bufferStride  Spacing between values to read from pSrcBuffer.
    2197             :  *                      The spacing is in number of array elements, not bytes.
    2198             :  *                      If provided, must contain GetDimensionCount() values.
    2199             :  *                      Negative values are possible (for example to reorder
    2200             :  *                      from bottom-to-top to top-to-bottom).
    2201             :  *                      If set to nullptr, will be set so that pSrcBuffer is
    2202             :  *                      written in a compact way, with elements of the last /
    2203             :  *                      fastest varying dimension being consecutive.
    2204             :  *
    2205             :  * @param bufferDataType Data type of values in pSrcBuffer.
    2206             :  *
    2207             :  * @param pSrcBuffer    User buffer to read the values from. Should be big
    2208             :  *                      enough to store the number of values indicated by
    2209             :  * count[] and with the spacing of bufferStride[].
    2210             :  *
    2211             :  * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
    2212             :  *                             validity of pSrcBuffer. pSrcBufferAllocStart
    2213             :  * should be the pointer returned by the malloc() or equivalent call used to
    2214             :  * allocate the buffer. It will generally be equal to pSrcBuffer (when
    2215             :  * bufferStride[] values are all positive), but not necessarily. If specified,
    2216             :  * nSrcBufferAllocSize should be also set to the appropriate value. If no
    2217             :  * validation is needed, nullptr can be passed.
    2218             :  *
    2219             :  * @param nSrcBufferAllocSize  Optional buffer size, that can be used to
    2220             :  * validate the validity of pSrcBuffer. This is the size of the buffer starting
    2221             :  * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
    2222             :  *                             set to the appropriate value.
    2223             :  *                             If no validation is needed, 0 can be passed.
    2224             :  *
    2225             :  * @return true in case of success.
    2226             :  */
    2227        1752 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
    2228             :                                 const size_t *count, const GInt64 *arrayStep,
    2229             :                                 const GPtrDiff_t *bufferStride,
    2230             :                                 const GDALExtendedDataType &bufferDataType,
    2231             :                                 const void *pSrcBuffer,
    2232             :                                 const void *pSrcBufferAllocStart,
    2233             :                                 size_t nSrcBufferAllocSize)
    2234             : {
    2235        1752 :     if (!bufferDataType.CanConvertTo(GetDataType()))
    2236             :     {
    2237           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2238             :                  "Buffer data type is not convertible to array data type");
    2239           0 :         return false;
    2240             :     }
    2241             : 
    2242        3504 :     std::vector<GInt64> tmp_arrayStep;
    2243        3504 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    2244        1752 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    2245             :                               bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
    2246             :                               nSrcBufferAllocSize, tmp_arrayStep,
    2247             :                               tmp_bufferStride))
    2248             :     {
    2249           0 :         return false;
    2250             :     }
    2251             : 
    2252        1752 :     return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
    2253        1752 :                   pSrcBuffer);
    2254             : }
    2255             : 
    2256             : /************************************************************************/
    2257             : /*                          GetTotalElementsCount()                     */
    2258             : /************************************************************************/
    2259             : 
    2260             : /** Return the total number of values in the array.
    2261             :  *
    2262             :  * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
    2263             :  * and GDALAttributeGetTotalElementsCount().
    2264             :  *
    2265             :  */
    2266        1022 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
    2267             : {
    2268        1022 :     const auto &dims = GetDimensions();
    2269        1022 :     if (dims.empty())
    2270         504 :         return 1;
    2271         518 :     GUInt64 nElts = 1;
    2272        1146 :     for (const auto &dim : dims)
    2273             :     {
    2274             :         try
    2275             :         {
    2276         628 :             nElts = (CPLSM(static_cast<GUInt64>(nElts)) *
    2277        1884 :                      CPLSM(static_cast<GUInt64>(dim->GetSize())))
    2278         628 :                         .v();
    2279             :         }
    2280           0 :         catch (...)
    2281             :         {
    2282           0 :             return 0;
    2283             :         }
    2284             :     }
    2285         518 :     return nElts;
    2286             : }
    2287             : 
    2288             : /************************************************************************/
    2289             : /*                           GetBlockSize()                             */
    2290             : /************************************************************************/
    2291             : 
    2292             : /** Return the "natural" block size of the array along all dimensions.
    2293             :  *
    2294             :  * Some drivers might organize the array in tiles/blocks and reading/writing
    2295             :  * aligned on those tile/block boundaries will be more efficient.
    2296             :  *
    2297             :  * The returned number of elements in the vector is the same as
    2298             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
    2299             :  * the natural block size along the considered dimension.
    2300             :  * "Flat" arrays will typically return a vector of values set to 0.
    2301             :  *
    2302             :  * The default implementation will return a vector of values set to 0.
    2303             :  *
    2304             :  * This method is used by GetProcessingChunkSize().
    2305             :  *
    2306             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
    2307             :  * theoretical case of a 32-bit platform, this might exceed its size_t
    2308             :  * allocation capabilities.
    2309             :  *
    2310             :  * This is the same as the C function GDALMDArrayGetBlockSize().
    2311             :  *
    2312             :  * @return the block size, in number of elements along each dimension.
    2313             :  */
    2314         216 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
    2315             : {
    2316         216 :     return std::vector<GUInt64>(GetDimensionCount());
    2317             : }
    2318             : 
    2319             : /************************************************************************/
    2320             : /*                       GetProcessingChunkSize()                       */
    2321             : /************************************************************************/
    2322             : 
    2323             : /** \brief Return an optimal chunk size for read/write operations, given the
    2324             :  * natural block size and memory constraints specified.
    2325             :  *
    2326             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
    2327             :  * multiple of those returned by GetBlockSize() (unless the block define by
    2328             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
    2329             :  * returned by this method).
    2330             :  *
    2331             :  * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
    2332             :  *
    2333             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
    2334             :  * chunk.
    2335             :  *
    2336             :  * @return the chunk size, in number of elements along each dimension.
    2337             :  */
    2338             : std::vector<size_t>
    2339          60 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
    2340             : {
    2341          60 :     const auto &dims = GetDimensions();
    2342          60 :     const auto &nDTSize = GetDataType().GetSize();
    2343          60 :     std::vector<size_t> anChunkSize;
    2344         120 :     auto blockSize = GetBlockSize();
    2345          60 :     CPLAssert(blockSize.size() == dims.size());
    2346          60 :     size_t nChunkSize = nDTSize;
    2347          60 :     bool bOverflow = false;
    2348          60 :     constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
    2349             :     // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
    2350             :     // [1, min(sizet_max, dim_size[i])]
    2351             :     // Also make sure that the product of all anChunkSize[i]) fits on size_t
    2352         173 :     for (size_t i = 0; i < dims.size(); i++)
    2353             :     {
    2354             :         const auto sizeDimI =
    2355         226 :             std::max(static_cast<size_t>(1),
    2356         226 :                      static_cast<size_t>(
    2357         226 :                          std::min(static_cast<GUInt64>(kSIZE_T_MAX),
    2358         113 :                                   std::min(blockSize[i], dims[i]->GetSize()))));
    2359         113 :         anChunkSize.push_back(sizeDimI);
    2360         113 :         if (nChunkSize > kSIZE_T_MAX / sizeDimI)
    2361             :         {
    2362           4 :             bOverflow = true;
    2363             :         }
    2364             :         else
    2365             :         {
    2366         109 :             nChunkSize *= sizeDimI;
    2367             :         }
    2368             :     }
    2369          60 :     if (nChunkSize == 0)
    2370           0 :         return anChunkSize;
    2371             : 
    2372             :     // If the product of all anChunkSize[i] does not fit on size_t, then
    2373             :     // set lowest anChunkSize[i] to 1.
    2374          60 :     if (bOverflow)
    2375             :     {
    2376           2 :         nChunkSize = nDTSize;
    2377           2 :         bOverflow = false;
    2378           8 :         for (size_t i = dims.size(); i > 0;)
    2379             :         {
    2380           6 :             --i;
    2381           6 :             if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
    2382             :             {
    2383           4 :                 bOverflow = true;
    2384           4 :                 anChunkSize[i] = 1;
    2385             :             }
    2386             :             else
    2387             :             {
    2388           2 :                 nChunkSize *= anChunkSize[i];
    2389             :             }
    2390             :         }
    2391             :     }
    2392             : 
    2393          60 :     nChunkSize = nDTSize;
    2394         120 :     std::vector<size_t> anAccBlockSizeFromStart;
    2395         173 :     for (size_t i = 0; i < dims.size(); i++)
    2396             :     {
    2397         113 :         nChunkSize *= anChunkSize[i];
    2398         113 :         anAccBlockSizeFromStart.push_back(nChunkSize);
    2399             :     }
    2400          60 :     if (nChunkSize <= nMaxChunkMemory / 2)
    2401             :     {
    2402          56 :         size_t nVoxelsFromEnd = 1;
    2403         161 :         for (size_t i = dims.size(); i > 0;)
    2404             :         {
    2405         105 :             --i;
    2406             :             const auto nCurBlockSize =
    2407         105 :                 anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
    2408         105 :             const auto nMul = nMaxChunkMemory / nCurBlockSize;
    2409         105 :             if (nMul >= 2)
    2410             :             {
    2411          97 :                 const auto nSizeThisDim(dims[i]->GetSize());
    2412             :                 const auto nBlocksThisDim =
    2413          97 :                     DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
    2414          97 :                 anChunkSize[i] = static_cast<size_t>(std::min(
    2415          97 :                     anChunkSize[i] *
    2416         194 :                         std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
    2417          97 :                     nSizeThisDim));
    2418             :             }
    2419         105 :             nVoxelsFromEnd *= anChunkSize[i];
    2420             :         }
    2421             :     }
    2422          60 :     return anChunkSize;
    2423             : }
    2424             : 
    2425             : /************************************************************************/
    2426             : /*                         BaseRename()                                 */
    2427             : /************************************************************************/
    2428             : 
    2429             : //! @cond Doxygen_Suppress
    2430          18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
    2431             : {
    2432          18 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
    2433          18 :     m_osFullName += osNewName;
    2434          18 :     m_osName = osNewName;
    2435             : 
    2436          18 :     NotifyChildrenOfRenaming();
    2437          18 : }
    2438             : 
    2439             : //! @endcond
    2440             : 
    2441             : //! @cond Doxygen_Suppress
    2442             : /************************************************************************/
    2443             : /*                          ParentRenamed()                             */
    2444             : /************************************************************************/
    2445             : 
    2446          50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
    2447             : {
    2448          50 :     m_osFullName = osNewParentFullName;
    2449          50 :     m_osFullName += "/";
    2450          50 :     m_osFullName += m_osName;
    2451             : 
    2452          50 :     NotifyChildrenOfRenaming();
    2453          50 : }
    2454             : 
    2455             : //! @endcond
    2456             : 
    2457             : /************************************************************************/
    2458             : /*                             Deleted()                                */
    2459             : /************************************************************************/
    2460             : 
    2461             : //! @cond Doxygen_Suppress
    2462          52 : void GDALAbstractMDArray::Deleted()
    2463             : {
    2464          52 :     m_bValid = false;
    2465             : 
    2466          52 :     NotifyChildrenOfDeletion();
    2467          52 : }
    2468             : 
    2469             : //! @endcond
    2470             : 
    2471             : /************************************************************************/
    2472             : /*                        ParentDeleted()                               */
    2473             : /************************************************************************/
    2474             : 
    2475             : //! @cond Doxygen_Suppress
    2476          28 : void GDALAbstractMDArray::ParentDeleted()
    2477             : {
    2478          28 :     Deleted();
    2479          28 : }
    2480             : 
    2481             : //! @endcond
    2482             : 
    2483             : /************************************************************************/
    2484             : /*                     CheckValidAndErrorOutIfNot()                     */
    2485             : /************************************************************************/
    2486             : 
    2487             : //! @cond Doxygen_Suppress
    2488        5617 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
    2489             : {
    2490        5617 :     if (!m_bValid)
    2491             :     {
    2492          26 :         CPLError(CE_Failure, CPLE_AppDefined,
    2493             :                  "This object has been deleted. No action on it is possible");
    2494             :     }
    2495        5617 :     return m_bValid;
    2496             : }
    2497             : 
    2498             : //! @endcond
    2499             : 
    2500             : /************************************************************************/
    2501             : /*                             SetUnit()                                */
    2502             : /************************************************************************/
    2503             : 
    2504             : /** Set the variable unit.
    2505             :  *
    2506             :  * Values should conform as much as possible with those allowed by
    2507             :  * the NetCDF CF conventions:
    2508             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
    2509             :  * but others might be returned.
    2510             :  *
    2511             :  * Few examples are "meter", "degrees", "second", ...
    2512             :  * Empty value means unknown.
    2513             :  *
    2514             :  * This is the same as the C function GDALMDArraySetUnit()
    2515             :  *
    2516             :  * @note Driver implementation: optionally implemented.
    2517             :  *
    2518             :  * @param osUnit unit name.
    2519             :  * @return true in case of success.
    2520             :  */
    2521           0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
    2522             : {
    2523           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
    2524           0 :     return false;
    2525             : }
    2526             : 
    2527             : /************************************************************************/
    2528             : /*                             GetUnit()                                */
    2529             : /************************************************************************/
    2530             : 
    2531             : /** Return the array unit.
    2532             :  *
    2533             :  * Values should conform as much as possible with those allowed by
    2534             :  * the NetCDF CF conventions:
    2535             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
    2536             :  * but others might be returned.
    2537             :  *
    2538             :  * Few examples are "meter", "degrees", "second", ...
    2539             :  * Empty value means unknown.
    2540             :  *
    2541             :  * This is the same as the C function GDALMDArrayGetUnit()
    2542             :  */
    2543           5 : const std::string &GDALMDArray::GetUnit() const
    2544             : {
    2545           5 :     static const std::string emptyString;
    2546           5 :     return emptyString;
    2547             : }
    2548             : 
    2549             : /************************************************************************/
    2550             : /*                          SetSpatialRef()                             */
    2551             : /************************************************************************/
    2552             : 
    2553             : /** Assign a spatial reference system object to the array.
    2554             :  *
    2555             :  * This is the same as the C function GDALMDArraySetSpatialRef().
    2556             :  */
    2557           0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
    2558             : {
    2559           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
    2560           0 :     return false;
    2561             : }
    2562             : 
    2563             : /************************************************************************/
    2564             : /*                          GetSpatialRef()                             */
    2565             : /************************************************************************/
    2566             : 
    2567             : /** Return the spatial reference system object associated with the array.
    2568             :  *
    2569             :  * This is the same as the C function GDALMDArrayGetSpatialRef().
    2570             :  */
    2571           4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
    2572             : {
    2573           4 :     return nullptr;
    2574             : }
    2575             : 
    2576             : /************************************************************************/
    2577             : /*                        GetRawNoDataValue()                           */
    2578             : /************************************************************************/
    2579             : 
    2580             : /** Return the nodata value as a "raw" value.
    2581             :  *
    2582             :  * The value returned might be nullptr in case of no nodata value. When
    2583             :  * a nodata value is registered, a non-nullptr will be returned whose size in
    2584             :  * bytes is GetDataType().GetSize().
    2585             :  *
    2586             :  * The returned value should not be modified or freed. It is valid until
    2587             :  * the array is destroyed, or the next call to GetRawNoDataValue() or
    2588             :  * SetRawNoDataValue(), or any similar methods.
    2589             :  *
    2590             :  * @note Driver implementation: this method shall be implemented if nodata
    2591             :  * is supported.
    2592             :  *
    2593             :  * This is the same as the C function GDALMDArrayGetRawNoDataValue().
    2594             :  *
    2595             :  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
    2596             :  */
    2597           5 : const void *GDALMDArray::GetRawNoDataValue() const
    2598             : {
    2599           5 :     return nullptr;
    2600             : }
    2601             : 
    2602             : /************************************************************************/
    2603             : /*                        GetNoDataValueAsDouble()                      */
    2604             : /************************************************************************/
    2605             : 
    2606             : /** Return the nodata value as a double.
    2607             :  *
    2608             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
    2609             :  *
    2610             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2611             :  * a nodata value exists and can be converted to double. Might be nullptr.
    2612             :  *
    2613             :  * @return the nodata value as a double. A 0.0 value might also indicate the
    2614             :  * absence of a nodata value or an error in the conversion (*pbHasNoData will be
    2615             :  * set to false then).
    2616             :  */
    2617       22417 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
    2618             : {
    2619       22417 :     const void *pNoData = GetRawNoDataValue();
    2620       22417 :     double dfNoData = 0.0;
    2621       22417 :     const auto &eDT = GetDataType();
    2622       22417 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2623       22417 :     if (ok)
    2624             :     {
    2625       22180 :         GDALCopyWords(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
    2626             :                       GDT_Float64, 0, 1);
    2627             :     }
    2628       22417 :     if (pbHasNoData)
    2629         384 :         *pbHasNoData = ok;
    2630       22417 :     return dfNoData;
    2631             : }
    2632             : 
    2633             : /************************************************************************/
    2634             : /*                        GetNoDataValueAsInt64()                       */
    2635             : /************************************************************************/
    2636             : 
    2637             : /** Return the nodata value as a Int64.
    2638             :  *
    2639             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2640             :  * a nodata value exists and can be converted to Int64. Might be nullptr.
    2641             :  *
    2642             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
    2643             :  *
    2644             :  * @return the nodata value as a Int64
    2645             :  *
    2646             :  * @since GDAL 3.5
    2647             :  */
    2648          12 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
    2649             : {
    2650          12 :     const void *pNoData = GetRawNoDataValue();
    2651          12 :     int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
    2652          12 :     const auto &eDT = GetDataType();
    2653          12 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2654          12 :     if (ok)
    2655             :     {
    2656           8 :         GDALCopyWords(pNoData, eDT.GetNumericDataType(), 0, &nNoData, GDT_Int64,
    2657             :                       0, 1);
    2658             :     }
    2659          12 :     if (pbHasNoData)
    2660          12 :         *pbHasNoData = ok;
    2661          12 :     return nNoData;
    2662             : }
    2663             : 
    2664             : /************************************************************************/
    2665             : /*                       GetNoDataValueAsUInt64()                       */
    2666             : /************************************************************************/
    2667             : 
    2668             : /** Return the nodata value as a UInt64.
    2669             :  *
    2670             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
    2671             : 
    2672             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2673             :  * a nodata value exists and can be converted to UInt64. Might be nullptr.
    2674             :  *
    2675             :  * @return the nodata value as a UInt64
    2676             :  *
    2677             :  * @since GDAL 3.5
    2678             :  */
    2679           8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
    2680             : {
    2681           8 :     const void *pNoData = GetRawNoDataValue();
    2682           8 :     uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
    2683           8 :     const auto &eDT = GetDataType();
    2684           8 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2685           8 :     if (ok)
    2686             :     {
    2687           6 :         GDALCopyWords(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
    2688             :                       GDT_UInt64, 0, 1);
    2689             :     }
    2690           8 :     if (pbHasNoData)
    2691           8 :         *pbHasNoData = ok;
    2692           8 :     return nNoData;
    2693             : }
    2694             : 
    2695             : /************************************************************************/
    2696             : /*                        SetRawNoDataValue()                           */
    2697             : /************************************************************************/
    2698             : 
    2699             : /** Set the nodata value as a "raw" value.
    2700             :  *
    2701             :  * The value passed might be nullptr in case of no nodata value. When
    2702             :  * a nodata value is registered, a non-nullptr whose size in
    2703             :  * bytes is GetDataType().GetSize() must be passed.
    2704             :  *
    2705             :  * This is the same as the C function GDALMDArraySetRawNoDataValue().
    2706             :  *
    2707             :  * @note Driver implementation: this method shall be implemented if setting
    2708             :  nodata
    2709             :  * is supported.
    2710             : 
    2711             :  * @return true in case of success.
    2712             :  */
    2713           0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
    2714             : {
    2715           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2716             :              "SetRawNoDataValue() not implemented");
    2717           0 :     return false;
    2718             : }
    2719             : 
    2720             : /************************************************************************/
    2721             : /*                           SetNoDataValue()                           */
    2722             : /************************************************************************/
    2723             : 
    2724             : /** Set the nodata value as a double.
    2725             :  *
    2726             :  * If the natural data type of the attribute/array is not double, type
    2727             :  * conversion will occur to the type returned by GetDataType().
    2728             :  *
    2729             :  * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
    2730             :  *
    2731             :  * @return true in case of success.
    2732             :  */
    2733          57 : bool GDALMDArray::SetNoDataValue(double dfNoData)
    2734             : {
    2735          57 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2736          57 :     bool bRet = false;
    2737          57 :     if (GDALExtendedDataType::CopyValue(
    2738         114 :             &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
    2739          57 :             GetDataType()))
    2740             :     {
    2741          57 :         bRet = SetRawNoDataValue(pRawNoData);
    2742             :     }
    2743          57 :     CPLFree(pRawNoData);
    2744          57 :     return bRet;
    2745             : }
    2746             : 
    2747             : /************************************************************************/
    2748             : /*                           SetNoDataValue()                           */
    2749             : /************************************************************************/
    2750             : 
    2751             : /** Set the nodata value as a Int64.
    2752             :  *
    2753             :  * If the natural data type of the attribute/array is not Int64, type conversion
    2754             :  * will occur to the type returned by GetDataType().
    2755             :  *
    2756             :  * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
    2757             :  *
    2758             :  * @return true in case of success.
    2759             :  *
    2760             :  * @since GDAL 3.5
    2761             :  */
    2762           3 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
    2763             : {
    2764           3 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2765           3 :     bool bRet = false;
    2766           3 :     if (GDALExtendedDataType::CopyValue(&nNoData,
    2767           6 :                                         GDALExtendedDataType::Create(GDT_Int64),
    2768           3 :                                         pRawNoData, GetDataType()))
    2769             :     {
    2770           3 :         bRet = SetRawNoDataValue(pRawNoData);
    2771             :     }
    2772           3 :     CPLFree(pRawNoData);
    2773           3 :     return bRet;
    2774             : }
    2775             : 
    2776             : /************************************************************************/
    2777             : /*                           SetNoDataValue()                           */
    2778             : /************************************************************************/
    2779             : 
    2780             : /** Set the nodata value as a Int64.
    2781             :  *
    2782             :  * If the natural data type of the attribute/array is not Int64, type conversion
    2783             :  * will occur to the type returned by GetDataType().
    2784             :  *
    2785             :  * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
    2786             :  *
    2787             :  * @return true in case of success.
    2788             :  *
    2789             :  * @since GDAL 3.5
    2790             :  */
    2791           1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
    2792             : {
    2793           1 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2794           1 :     bool bRet = false;
    2795           1 :     if (GDALExtendedDataType::CopyValue(
    2796           2 :             &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
    2797           1 :             GetDataType()))
    2798             :     {
    2799           1 :         bRet = SetRawNoDataValue(pRawNoData);
    2800             :     }
    2801           1 :     CPLFree(pRawNoData);
    2802           1 :     return bRet;
    2803             : }
    2804             : 
    2805             : /************************************************************************/
    2806             : /*                            Resize()                                  */
    2807             : /************************************************************************/
    2808             : 
    2809             : /** Resize an array to new dimensions.
    2810             :  *
    2811             :  * Not all drivers may allow this operation, and with restrictions (e.g.
    2812             :  * for netCDF, this is limited to growing of "unlimited" dimensions)
    2813             :  *
    2814             :  * Resizing a dimension used in other arrays will cause those other arrays
    2815             :  * to be resized.
    2816             :  *
    2817             :  * This is the same as the C function GDALMDArrayResize().
    2818             :  *
    2819             :  * @param anNewDimSizes Array of GetDimensionCount() values containing the
    2820             :  *                      new size of each indexing dimension.
    2821             :  * @param papszOptions Options. (Driver specific)
    2822             :  * @return true in case of success.
    2823             :  * @since GDAL 3.7
    2824             :  */
    2825           0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
    2826             :                          CPL_UNUSED CSLConstList papszOptions)
    2827             : {
    2828           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2829             :              "Resize() is not supported for this array");
    2830           0 :     return false;
    2831             : }
    2832             : 
    2833             : /************************************************************************/
    2834             : /*                               SetScale()                             */
    2835             : /************************************************************************/
    2836             : 
    2837             : /** Set the scale value to apply to raw values.
    2838             :  *
    2839             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2840             :  *
    2841             :  * This is the same as the C function GDALMDArraySetScale() /
    2842             :  * GDALMDArraySetScaleEx().
    2843             :  *
    2844             :  * @note Driver implementation: this method shall be implemented if setting
    2845             :  * scale is supported.
    2846             :  *
    2847             :  * @param dfScale scale
    2848             :  * @param eStorageType Data type to which create the potential attribute that
    2849             :  * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
    2850             :  * implementation will decide automatically the data type. Note that changing
    2851             :  * the data type after initial setting might not be supported.
    2852             :  * @return true in case of success.
    2853             :  */
    2854           0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
    2855             :                            CPL_UNUSED GDALDataType eStorageType)
    2856             : {
    2857           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
    2858           0 :     return false;
    2859             : }
    2860             : 
    2861             : /************************************************************************/
    2862             : /*                               SetOffset)                             */
    2863             : /************************************************************************/
    2864             : 
    2865             : /** Set the offset value to apply to raw values.
    2866             :  *
    2867             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2868             :  *
    2869             :  * This is the same as the C function GDALMDArraySetOffset() /
    2870             :  * GDALMDArraySetOffsetEx().
    2871             :  *
    2872             :  * @note Driver implementation: this method shall be implemented if setting
    2873             :  * offset is supported.
    2874             :  *
    2875             :  * @param dfOffset Offset
    2876             :  * @param eStorageType Data type to which create the potential attribute that
    2877             :  * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
    2878             :  * implementation will decide automatically the data type. Note that changing
    2879             :  * the data type after initial setting might not be supported.
    2880             :  * @return true in case of success.
    2881             :  */
    2882           0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
    2883             :                             CPL_UNUSED GDALDataType eStorageType)
    2884             : {
    2885           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
    2886           0 :     return false;
    2887             : }
    2888             : 
    2889             : /************************************************************************/
    2890             : /*                               GetScale()                             */
    2891             : /************************************************************************/
    2892             : 
    2893             : /** Get the scale value to apply to raw values.
    2894             :  *
    2895             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2896             :  *
    2897             :  * This is the same as the C function GDALMDArrayGetScale().
    2898             :  *
    2899             :  * @note Driver implementation: this method shall be implemented if gettings
    2900             :  * scale is supported.
    2901             :  *
    2902             :  * @param pbHasScale Pointer to a output boolean that will be set to true if
    2903             :  * a scale value exists. Might be nullptr.
    2904             :  * @param peStorageType Pointer to a output GDALDataType that will be set to
    2905             :  * the storage type of the scale value, when known/relevant. Otherwise will be
    2906             :  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
    2907             :  *
    2908             :  * @return the scale value. A 1.0 value might also indicate the
    2909             :  * absence of a scale value.
    2910             :  */
    2911          13 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
    2912             :                              CPL_UNUSED GDALDataType *peStorageType) const
    2913             : {
    2914          13 :     if (pbHasScale)
    2915          13 :         *pbHasScale = false;
    2916          13 :     return 1.0;
    2917             : }
    2918             : 
    2919             : /************************************************************************/
    2920             : /*                               GetOffset()                            */
    2921             : /************************************************************************/
    2922             : 
    2923             : /** Get the offset value to apply to raw values.
    2924             :  *
    2925             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2926             :  *
    2927             :  * This is the same as the C function GDALMDArrayGetOffset().
    2928             :  *
    2929             :  * @note Driver implementation: this method shall be implemented if gettings
    2930             :  * offset is supported.
    2931             :  *
    2932             :  * @param pbHasOffset Pointer to a output boolean that will be set to true if
    2933             :  * a offset value exists. Might be nullptr.
    2934             :  * @param peStorageType Pointer to a output GDALDataType that will be set to
    2935             :  * the storage type of the offset value, when known/relevant. Otherwise will be
    2936             :  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
    2937             :  *
    2938             :  * @return the offset value. A 0.0 value might also indicate the
    2939             :  * absence of a offset value.
    2940             :  */
    2941          13 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
    2942             :                               CPL_UNUSED GDALDataType *peStorageType) const
    2943             : {
    2944          13 :     if (pbHasOffset)
    2945          13 :         *pbHasOffset = false;
    2946          13 :     return 0.0;
    2947             : }
    2948             : 
    2949             : /************************************************************************/
    2950             : /*                         ProcessPerChunk()                            */
    2951             : /************************************************************************/
    2952             : 
    2953             : namespace
    2954             : {
    2955             : enum class Caller
    2956             : {
    2957             :     CALLER_END_OF_LOOP,
    2958             :     CALLER_IN_LOOP,
    2959             : };
    2960             : }
    2961             : 
    2962             : /** \brief Call a user-provided function to operate on an array chunk by chunk.
    2963             :  *
    2964             :  * This method is to be used when doing operations on an array, or a subset of
    2965             :  * it, in a chunk by chunk way.
    2966             :  *
    2967             :  * @param arrayStartIdx Values representing the starting index to use
    2968             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2969             :  *                      Array of GetDimensionCount() values. Must not be
    2970             :  *                      nullptr, unless for a zero-dimensional array.
    2971             :  *
    2972             :  * @param count         Values representing the number of values to use in
    2973             :  *                      each dimension.
    2974             :  *                      Array of GetDimensionCount() values. Must not be
    2975             :  *                      nullptr, unless for a zero-dimensional array.
    2976             :  *
    2977             :  * @param chunkSize     Values representing the chunk size in each dimension.
    2978             :  *                      Might typically the output of GetProcessingChunkSize().
    2979             :  *                      Array of GetDimensionCount() values. Must not be
    2980             :  *                      nullptr, unless for a zero-dimensional array.
    2981             :  *
    2982             :  * @param pfnFunc       User-provided function of type FuncProcessPerChunkType.
    2983             :  *                      Must NOT be nullptr.
    2984             :  *
    2985             :  * @param pUserData     Pointer to pass as the value of the pUserData argument
    2986             :  * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
    2987             :  *
    2988             :  * @return true in case of success.
    2989             :  */
    2990          58 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
    2991             :                                           const GUInt64 *count,
    2992             :                                           const size_t *chunkSize,
    2993             :                                           FuncProcessPerChunkType pfnFunc,
    2994             :                                           void *pUserData)
    2995             : {
    2996          58 :     const auto &dims = GetDimensions();
    2997          58 :     if (dims.empty())
    2998             :     {
    2999           2 :         return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
    3000             :     }
    3001             : 
    3002             :     // Sanity check
    3003          56 :     size_t nTotalChunkSize = 1;
    3004         146 :     for (size_t i = 0; i < dims.size(); i++)
    3005             :     {
    3006          97 :         const auto nSizeThisDim(dims[i]->GetSize());
    3007          97 :         if (count[i] == 0 || count[i] > nSizeThisDim ||
    3008          95 :             arrayStartIdx[i] > nSizeThisDim - count[i])
    3009             :         {
    3010           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    3011             :                      "Inconsistent arrayStartIdx[] / count[] values "
    3012             :                      "regarding array size");
    3013           4 :             return false;
    3014             :         }
    3015         184 :         if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
    3016          91 :             chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
    3017             :         {
    3018           3 :             CPLError(CE_Failure, CPLE_AppDefined,
    3019             :                      "Inconsistent chunkSize[] values");
    3020           3 :             return false;
    3021             :         }
    3022          90 :         nTotalChunkSize *= chunkSize[i];
    3023             :     }
    3024             : 
    3025          49 :     size_t dimIdx = 0;
    3026          98 :     std::vector<GUInt64> chunkArrayStartIdx(dims.size());
    3027          98 :     std::vector<size_t> chunkCount(dims.size());
    3028             : 
    3029             :     struct Stack
    3030             :     {
    3031             :         GUInt64 nBlockCounter = 0;
    3032             :         GUInt64 nBlocksMinusOne = 0;
    3033             :         size_t first_count = 0;  // only used if nBlocks > 1
    3034             :         Caller return_point = Caller::CALLER_END_OF_LOOP;
    3035             :     };
    3036             : 
    3037          98 :     std::vector<Stack> stack(dims.size());
    3038          49 :     GUInt64 iCurChunk = 0;
    3039          49 :     GUInt64 nChunkCount = 1;
    3040         138 :     for (size_t i = 0; i < dims.size(); i++)
    3041             :     {
    3042          89 :         const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
    3043          89 :         const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
    3044          89 :         stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
    3045          89 :         nChunkCount *= 1 + stack[i].nBlocksMinusOne;
    3046          89 :         if (stack[i].nBlocksMinusOne == 0)
    3047             :         {
    3048          84 :             chunkArrayStartIdx[i] = arrayStartIdx[i];
    3049          84 :             chunkCount[i] = static_cast<size_t>(count[i]);
    3050             :         }
    3051             :         else
    3052             :         {
    3053           5 :             stack[i].first_count = static_cast<size_t>(
    3054           5 :                 (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
    3055             :         }
    3056             :     }
    3057             : 
    3058          49 : lbl_next_depth:
    3059         248 :     if (dimIdx == dims.size())
    3060             :     {
    3061          82 :         ++iCurChunk;
    3062          82 :         if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
    3063             :                      iCurChunk, nChunkCount, pUserData))
    3064             :         {
    3065           0 :             return false;
    3066             :         }
    3067             :     }
    3068             :     else
    3069             :     {
    3070         166 :         if (stack[dimIdx].nBlocksMinusOne != 0)
    3071             :         {
    3072          11 :             stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
    3073          11 :             chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
    3074          11 :             chunkCount[dimIdx] = stack[dimIdx].first_count;
    3075          11 :             stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
    3076             :             while (true)
    3077             :             {
    3078          33 :                 dimIdx++;
    3079          33 :                 goto lbl_next_depth;
    3080          33 :             lbl_return_to_caller_in_loop:
    3081          33 :                 --stack[dimIdx].nBlockCounter;
    3082          33 :                 if (stack[dimIdx].nBlockCounter == 0)
    3083          11 :                     break;
    3084          22 :                 chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    3085          22 :                 chunkCount[dimIdx] = chunkSize[dimIdx];
    3086             :             }
    3087             : 
    3088          11 :             chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    3089          22 :             chunkCount[dimIdx] =
    3090          11 :                 static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
    3091          11 :                                     chunkArrayStartIdx[dimIdx]);
    3092          11 :             stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
    3093             :         }
    3094         166 :         dimIdx++;
    3095         166 :         goto lbl_next_depth;
    3096         166 :     lbl_return_to_caller_end_of_loop:
    3097         166 :         if (dimIdx == 0)
    3098          49 :             goto end;
    3099             :     }
    3100             : 
    3101         199 :     assert(dimIdx > 0);
    3102         199 :     dimIdx--;
    3103             :     // cppcheck-suppress negativeContainerIndex
    3104         199 :     switch (stack[dimIdx].return_point)
    3105             :     {
    3106         166 :         case Caller::CALLER_END_OF_LOOP:
    3107         166 :             goto lbl_return_to_caller_end_of_loop;
    3108          33 :         case Caller::CALLER_IN_LOOP:
    3109          33 :             goto lbl_return_to_caller_in_loop;
    3110             :     }
    3111          49 : end:
    3112          49 :     return true;
    3113             : }
    3114             : 
    3115             : /************************************************************************/
    3116             : /*                          GDALAttribute()                             */
    3117             : /************************************************************************/
    3118             : 
    3119             : //! @cond Doxygen_Suppress
    3120       13997 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
    3121           0 :                              CPL_UNUSED const std::string &osName)
    3122             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
    3123       13997 :     : GDALAbstractMDArray(osParentName, osName)
    3124             : #endif
    3125             : {
    3126       13997 : }
    3127             : 
    3128             : //! @endcond
    3129             : 
    3130             : /************************************************************************/
    3131             : /*                        GetDimensionSize()                            */
    3132             : /************************************************************************/
    3133             : 
    3134             : /** Return the size of the dimensions of the attribute.
    3135             :  *
    3136             :  * This will be an empty array for a scalar (single value) attribute.
    3137             :  *
    3138             :  * This is the same as the C function GDALAttributeGetDimensionsSize().
    3139             :  */
    3140         361 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
    3141             : {
    3142         361 :     const auto &dims = GetDimensions();
    3143         361 :     std::vector<GUInt64> ret;
    3144         361 :     ret.reserve(dims.size());
    3145         453 :     for (const auto &dim : dims)
    3146          92 :         ret.push_back(dim->GetSize());
    3147         361 :     return ret;
    3148             : }
    3149             : 
    3150             : /************************************************************************/
    3151             : /*                            GDALRawResult()                           */
    3152             : /************************************************************************/
    3153             : 
    3154             : //! @cond Doxygen_Suppress
    3155         149 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
    3156         149 :                              size_t nEltCount)
    3157         298 :     : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
    3158         149 :       m_raw(raw)
    3159             : {
    3160         149 : }
    3161             : 
    3162             : //! @endcond
    3163             : 
    3164             : /************************************************************************/
    3165             : /*                            GDALRawResult()                           */
    3166             : /************************************************************************/
    3167             : 
    3168             : /** Move constructor. */
    3169           0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
    3170           0 :     : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
    3171           0 :       m_nSize(other.m_nSize), m_raw(other.m_raw)
    3172             : {
    3173           0 :     other.m_nEltCount = 0;
    3174           0 :     other.m_nSize = 0;
    3175           0 :     other.m_raw = nullptr;
    3176           0 : }
    3177             : 
    3178             : /************************************************************************/
    3179             : /*                               FreeMe()                               */
    3180             : /************************************************************************/
    3181             : 
    3182         149 : void GDALRawResult::FreeMe()
    3183             : {
    3184         149 :     if (m_raw && m_dt.NeedsFreeDynamicMemory())
    3185             :     {
    3186          47 :         GByte *pabyPtr = m_raw;
    3187          47 :         const auto nDTSize(m_dt.GetSize());
    3188          94 :         for (size_t i = 0; i < m_nEltCount; ++i)
    3189             :         {
    3190          47 :             m_dt.FreeDynamicMemory(pabyPtr);
    3191          47 :             pabyPtr += nDTSize;
    3192             :         }
    3193             :     }
    3194         149 :     VSIFree(m_raw);
    3195         149 : }
    3196             : 
    3197             : /************************************************************************/
    3198             : /*                             operator=()                              */
    3199             : /************************************************************************/
    3200             : 
    3201             : /** Move assignment. */
    3202           0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
    3203             : {
    3204           0 :     FreeMe();
    3205           0 :     m_dt = std::move(other.m_dt);
    3206           0 :     m_nEltCount = other.m_nEltCount;
    3207           0 :     m_nSize = other.m_nSize;
    3208           0 :     m_raw = other.m_raw;
    3209           0 :     other.m_nEltCount = 0;
    3210           0 :     other.m_nSize = 0;
    3211           0 :     other.m_raw = nullptr;
    3212           0 :     return *this;
    3213             : }
    3214             : 
    3215             : /************************************************************************/
    3216             : /*                         ~GDALRawResult()                             */
    3217             : /************************************************************************/
    3218             : 
    3219             : /** Destructor. */
    3220         149 : GDALRawResult::~GDALRawResult()
    3221             : {
    3222         149 :     FreeMe();
    3223         149 : }
    3224             : 
    3225             : /************************************************************************/
    3226             : /*                            StealData()                               */
    3227             : /************************************************************************/
    3228             : 
    3229             : //! @cond Doxygen_Suppress
    3230             : /** Return buffer to caller which becomes owner of it.
    3231             :  * Only to be used by GDALAttributeReadAsRaw().
    3232             :  */
    3233           6 : GByte *GDALRawResult::StealData()
    3234             : {
    3235           6 :     GByte *ret = m_raw;
    3236           6 :     m_raw = nullptr;
    3237           6 :     m_nEltCount = 0;
    3238           6 :     m_nSize = 0;
    3239           6 :     return ret;
    3240             : }
    3241             : 
    3242             : //! @endcond
    3243             : 
    3244             : /************************************************************************/
    3245             : /*                             ReadAsRaw()                              */
    3246             : /************************************************************************/
    3247             : 
    3248             : /** Return the raw value of an attribute.
    3249             :  *
    3250             :  *
    3251             :  * This is the same as the C function GDALAttributeReadAsRaw().
    3252             :  */
    3253         149 : GDALRawResult GDALAttribute::ReadAsRaw() const
    3254             : {
    3255         149 :     const auto nEltCount(GetTotalElementsCount());
    3256         149 :     const auto &dt(GetDataType());
    3257         149 :     const auto nDTSize(dt.GetSize());
    3258             :     GByte *res = static_cast<GByte *>(
    3259         149 :         VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
    3260         149 :     if (!res)
    3261           0 :         return GDALRawResult(nullptr, dt, 0);
    3262         149 :     const auto &dims = GetDimensions();
    3263         149 :     const auto nDims = GetDimensionCount();
    3264         298 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3265         298 :     std::vector<size_t> count(1 + nDims);
    3266         168 :     for (size_t i = 0; i < nDims; i++)
    3267             :     {
    3268          19 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3269             :     }
    3270         149 :     if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
    3271         149 :               &res[0], static_cast<size_t>(nEltCount * nDTSize)))
    3272             :     {
    3273           0 :         VSIFree(res);
    3274           0 :         return GDALRawResult(nullptr, dt, 0);
    3275             :     }
    3276         149 :     return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
    3277             : }
    3278             : 
    3279             : /************************************************************************/
    3280             : /*                            ReadAsString()                            */
    3281             : /************************************************************************/
    3282             : 
    3283             : /** Return the value of an attribute as a string.
    3284             :  *
    3285             :  * The returned string should not be freed, and its lifetime does not
    3286             :  * excess a next call to ReadAsString() on the same object, or the deletion
    3287             :  * of the object itself.
    3288             :  *
    3289             :  * This function will only return the first element if there are several.
    3290             :  *
    3291             :  * This is the same as the C function GDALAttributeReadAsString()
    3292             :  *
    3293             :  * @return a string, or nullptr.
    3294             :  */
    3295        1301 : const char *GDALAttribute::ReadAsString() const
    3296             : {
    3297        1301 :     const auto nDims = GetDimensionCount();
    3298        2602 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3299        2602 :     std::vector<size_t> count(1 + nDims, 1);
    3300        1301 :     char *szRet = nullptr;
    3301        1301 :     if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
    3302        1301 :               GDALExtendedDataType::CreateString(), &szRet, &szRet,
    3303        3902 :               sizeof(szRet)) ||
    3304        1300 :         szRet == nullptr)
    3305             :     {
    3306           4 :         return nullptr;
    3307             :     }
    3308        1297 :     m_osCachedVal = szRet;
    3309        1297 :     CPLFree(szRet);
    3310        1297 :     return m_osCachedVal.c_str();
    3311             : }
    3312             : 
    3313             : /************************************************************************/
    3314             : /*                            ReadAsInt()                               */
    3315             : /************************************************************************/
    3316             : 
    3317             : /** Return the value of an attribute as a integer.
    3318             :  *
    3319             :  * This function will only return the first element if there are several.
    3320             :  *
    3321             :  * It can fail if its value can not be converted to integer.
    3322             :  *
    3323             :  * This is the same as the C function GDALAttributeReadAsInt()
    3324             :  *
    3325             :  * @return a integer, or INT_MIN in case of error.
    3326             :  */
    3327         218 : int GDALAttribute::ReadAsInt() const
    3328             : {
    3329         218 :     const auto nDims = GetDimensionCount();
    3330         436 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3331         218 :     std::vector<size_t> count(1 + nDims, 1);
    3332         218 :     int nRet = INT_MIN;
    3333         218 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3334         436 :          GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
    3335         436 :     return nRet;
    3336             : }
    3337             : 
    3338             : /************************************************************************/
    3339             : /*                            ReadAsInt64()                             */
    3340             : /************************************************************************/
    3341             : 
    3342             : /** Return the value of an attribute as an int64_t.
    3343             :  *
    3344             :  * This function will only return the first element if there are several.
    3345             :  *
    3346             :  * It can fail if its value can not be converted to long.
    3347             :  *
    3348             :  * This is the same as the C function GDALAttributeReadAsInt64()
    3349             :  *
    3350             :  * @return an int64_t, or INT64_MIN in case of error.
    3351             :  */
    3352          54 : int64_t GDALAttribute::ReadAsInt64() const
    3353             : {
    3354          54 :     const auto nDims = GetDimensionCount();
    3355         108 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3356          54 :     std::vector<size_t> count(1 + nDims, 1);
    3357          54 :     int64_t nRet = INT64_MIN;
    3358          54 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3359         108 :          GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
    3360         108 :     return nRet;
    3361             : }
    3362             : 
    3363             : /************************************************************************/
    3364             : /*                            ReadAsDouble()                            */
    3365             : /************************************************************************/
    3366             : 
    3367             : /** Return the value of an attribute as a double.
    3368             :  *
    3369             :  * This function will only return the first element if there are several.
    3370             :  *
    3371             :  * It can fail if its value can not be converted to double.
    3372             :  *
    3373             :  * This is the same as the C function GDALAttributeReadAsInt()
    3374             :  *
    3375             :  * @return a double value.
    3376             :  */
    3377         339 : double GDALAttribute::ReadAsDouble() const
    3378             : {
    3379         339 :     const auto nDims = GetDimensionCount();
    3380         678 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3381         339 :     std::vector<size_t> count(1 + nDims, 1);
    3382         339 :     double dfRet = 0;
    3383         339 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3384         339 :          GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
    3385         339 :          sizeof(dfRet));
    3386         678 :     return dfRet;
    3387             : }
    3388             : 
    3389             : /************************************************************************/
    3390             : /*                          ReadAsStringArray()                         */
    3391             : /************************************************************************/
    3392             : 
    3393             : /** Return the value of an attribute as an array of strings.
    3394             :  *
    3395             :  * This is the same as the C function GDALAttributeReadAsStringArray()
    3396             :  */
    3397         104 : CPLStringList GDALAttribute::ReadAsStringArray() const
    3398             : {
    3399         104 :     const auto nElts = GetTotalElementsCount();
    3400         104 :     if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
    3401           0 :         return CPLStringList();
    3402             :     char **papszList = static_cast<char **>(
    3403         104 :         VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
    3404         104 :     const auto &dims = GetDimensions();
    3405         104 :     const auto nDims = GetDimensionCount();
    3406         208 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3407         208 :     std::vector<size_t> count(1 + nDims);
    3408         157 :     for (size_t i = 0; i < nDims; i++)
    3409             :     {
    3410          53 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3411             :     }
    3412         104 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3413         104 :          GDALExtendedDataType::CreateString(), papszList, papszList,
    3414         104 :          sizeof(char *) * static_cast<int>(nElts));
    3415         269 :     for (int i = 0; i < static_cast<int>(nElts); i++)
    3416             :     {
    3417         165 :         if (papszList[i] == nullptr)
    3418          13 :             papszList[i] = CPLStrdup("");
    3419             :     }
    3420         104 :     return CPLStringList(papszList);
    3421             : }
    3422             : 
    3423             : /************************************************************************/
    3424             : /*                          ReadAsIntArray()                            */
    3425             : /************************************************************************/
    3426             : 
    3427             : /** Return the value of an attribute as an array of integers.
    3428             :  *
    3429             :  * This is the same as the C function GDALAttributeReadAsIntArray().
    3430             :  */
    3431          15 : std::vector<int> GDALAttribute::ReadAsIntArray() const
    3432             : {
    3433          15 :     const auto nElts = GetTotalElementsCount();
    3434             : #if SIZEOF_VOIDP == 4
    3435             :     if (nElts > static_cast<size_t>(nElts))
    3436             :         return {};
    3437             : #endif
    3438          15 :     std::vector<int> res(static_cast<size_t>(nElts));
    3439          15 :     const auto &dims = GetDimensions();
    3440          15 :     const auto nDims = GetDimensionCount();
    3441          30 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3442          30 :     std::vector<size_t> count(1 + nDims);
    3443          32 :     for (size_t i = 0; i < nDims; i++)
    3444             :     {
    3445          17 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3446             :     }
    3447          15 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3448          30 :          GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
    3449          15 :          res.size() * sizeof(res[0]));
    3450          30 :     return res;
    3451             : }
    3452             : 
    3453             : /************************************************************************/
    3454             : /*                          ReadAsInt64Array()                          */
    3455             : /************************************************************************/
    3456             : 
    3457             : /** Return the value of an attribute as an array of int64_t.
    3458             :  *
    3459             :  * This is the same as the C function GDALAttributeReadAsInt64Array().
    3460             :  */
    3461          38 : std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
    3462             : {
    3463          38 :     const auto nElts = GetTotalElementsCount();
    3464             : #if SIZEOF_VOIDP == 4
    3465             :     if (nElts > static_cast<size_t>(nElts))
    3466             :         return {};
    3467             : #endif
    3468          38 :     std::vector<int64_t> res(static_cast<size_t>(nElts));
    3469          38 :     const auto &dims = GetDimensions();
    3470          38 :     const auto nDims = GetDimensionCount();
    3471          76 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3472          76 :     std::vector<size_t> count(1 + nDims);
    3473          76 :     for (size_t i = 0; i < nDims; i++)
    3474             :     {
    3475          38 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3476             :     }
    3477          38 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3478          76 :          GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
    3479          38 :          res.size() * sizeof(res[0]));
    3480          76 :     return res;
    3481             : }
    3482             : 
    3483             : /************************************************************************/
    3484             : /*                         ReadAsDoubleArray()                          */
    3485             : /************************************************************************/
    3486             : 
    3487             : /** Return the value of an attribute as an array of double.
    3488             :  *
    3489             :  * This is the same as the C function GDALAttributeReadAsDoubleArray().
    3490             :  */
    3491          87 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
    3492             : {
    3493          87 :     const auto nElts = GetTotalElementsCount();
    3494             : #if SIZEOF_VOIDP == 4
    3495             :     if (nElts > static_cast<size_t>(nElts))
    3496             :         return {};
    3497             : #endif
    3498          87 :     std::vector<double> res(static_cast<size_t>(nElts));
    3499          87 :     const auto &dims = GetDimensions();
    3500          87 :     const auto nDims = GetDimensionCount();
    3501         174 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3502         174 :     std::vector<size_t> count(1 + nDims);
    3503         158 :     for (size_t i = 0; i < nDims; i++)
    3504             :     {
    3505          71 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3506             :     }
    3507          87 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3508         174 :          GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
    3509          87 :          res.size() * sizeof(res[0]));
    3510         174 :     return res;
    3511             : }
    3512             : 
    3513             : /************************************************************************/
    3514             : /*                               Write()                                */
    3515             : /************************************************************************/
    3516             : 
    3517             : /** Write an attribute from raw values expressed in GetDataType()
    3518             :  *
    3519             :  * The values should be provided in the type of GetDataType() and there should
    3520             :  * be exactly GetTotalElementsCount() of them.
    3521             :  * If GetDataType() is a string, each value should be a char* pointer.
    3522             :  *
    3523             :  * This is the same as the C function GDALAttributeWriteRaw().
    3524             :  *
    3525             :  * @param pabyValue Buffer of nLen bytes.
    3526             :  * @param nLen Size of pabyValue in bytes. Should be equal to
    3527             :  *             GetTotalElementsCount() * GetDataType().GetSize()
    3528             :  * @return true in case of success.
    3529             :  */
    3530          91 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
    3531             : {
    3532          91 :     if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
    3533             :     {
    3534           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3535             :                  "Length is not of expected value");
    3536           0 :         return false;
    3537             :     }
    3538          91 :     const auto &dims = GetDimensions();
    3539          91 :     const auto nDims = GetDimensionCount();
    3540         182 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3541         182 :     std::vector<size_t> count(1 + nDims);
    3542         114 :     for (size_t i = 0; i < nDims; i++)
    3543             :     {
    3544          23 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3545             :     }
    3546          91 :     return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
    3547          91 :                  pabyValue, pabyValue, nLen);
    3548             : }
    3549             : 
    3550             : /************************************************************************/
    3551             : /*                               Write()                                */
    3552             : /************************************************************************/
    3553             : 
    3554             : /** Write an attribute from a string value.
    3555             :  *
    3556             :  * Type conversion will be performed if needed. If the attribute contains
    3557             :  * multiple values, only the first one will be updated.
    3558             :  *
    3559             :  * This is the same as the C function GDALAttributeWriteString().
    3560             :  *
    3561             :  * @param pszValue Pointer to a string.
    3562             :  * @return true in case of success.
    3563             :  */
    3564         303 : bool GDALAttribute::Write(const char *pszValue)
    3565             : {
    3566         303 :     const auto nDims = GetDimensionCount();
    3567         606 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3568         303 :     std::vector<size_t> count(1 + nDims, 1);
    3569         303 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3570         606 :                  GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
    3571         606 :                  sizeof(pszValue));
    3572             : }
    3573             : 
    3574             : /************************************************************************/
    3575             : /*                              WriteInt()                              */
    3576             : /************************************************************************/
    3577             : 
    3578             : /** Write an attribute from a integer value.
    3579             :  *
    3580             :  * Type conversion will be performed if needed. If the attribute contains
    3581             :  * multiple values, only the first one will be updated.
    3582             :  *
    3583             :  * This is the same as the C function GDALAttributeWriteInt().
    3584             :  *
    3585             :  * @param nVal Value.
    3586             :  * @return true in case of success.
    3587             :  */
    3588          22 : bool GDALAttribute::WriteInt(int nVal)
    3589             : {
    3590          22 :     const auto nDims = GetDimensionCount();
    3591          44 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3592          22 :     std::vector<size_t> count(1 + nDims, 1);
    3593          22 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3594          44 :                  GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
    3595          44 :                  sizeof(nVal));
    3596             : }
    3597             : 
    3598             : /************************************************************************/
    3599             : /*                              WriteInt64()                             */
    3600             : /************************************************************************/
    3601             : 
    3602             : /** Write an attribute from an int64_t value.
    3603             :  *
    3604             :  * Type conversion will be performed if needed. If the attribute contains
    3605             :  * multiple values, only the first one will be updated.
    3606             :  *
    3607             :  * This is the same as the C function GDALAttributeWriteInt().
    3608             :  *
    3609             :  * @param nVal Value.
    3610             :  * @return true in case of success.
    3611             :  */
    3612          11 : bool GDALAttribute::WriteInt64(int64_t nVal)
    3613             : {
    3614          11 :     const auto nDims = GetDimensionCount();
    3615          22 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3616          11 :     std::vector<size_t> count(1 + nDims, 1);
    3617          11 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3618          22 :                  GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
    3619          22 :                  sizeof(nVal));
    3620             : }
    3621             : 
    3622             : /************************************************************************/
    3623             : /*                                Write()                               */
    3624             : /************************************************************************/
    3625             : 
    3626             : /** Write an attribute from a double value.
    3627             :  *
    3628             :  * Type conversion will be performed if needed. If the attribute contains
    3629             :  * multiple values, only the first one will be updated.
    3630             :  *
    3631             :  * This is the same as the C function GDALAttributeWriteDouble().
    3632             :  *
    3633             :  * @param dfVal Value.
    3634             :  * @return true in case of success.
    3635             :  */
    3636          36 : bool GDALAttribute::Write(double dfVal)
    3637             : {
    3638          36 :     const auto nDims = GetDimensionCount();
    3639          72 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3640          36 :     std::vector<size_t> count(1 + nDims, 1);
    3641          36 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3642          72 :                  GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
    3643          72 :                  sizeof(dfVal));
    3644             : }
    3645             : 
    3646             : /************************************************************************/
    3647             : /*                                Write()                               */
    3648             : /************************************************************************/
    3649             : 
    3650             : /** Write an attribute from an array of strings.
    3651             :  *
    3652             :  * Type conversion will be performed if needed.
    3653             :  *
    3654             :  * Exactly GetTotalElementsCount() strings must be provided
    3655             :  *
    3656             :  * This is the same as the C function GDALAttributeWriteStringArray().
    3657             :  *
    3658             :  * @param vals Array of strings.
    3659             :  * @return true in case of success.
    3660             :  */
    3661           8 : bool GDALAttribute::Write(CSLConstList vals)
    3662             : {
    3663           8 :     if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
    3664             :     {
    3665           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3666           1 :         return false;
    3667             :     }
    3668           7 :     const auto nDims = GetDimensionCount();
    3669          14 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3670           7 :     std::vector<size_t> count(1 + nDims);
    3671           7 :     const auto &dims = GetDimensions();
    3672          15 :     for (size_t i = 0; i < nDims; i++)
    3673           8 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3674           7 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3675           7 :                  GDALExtendedDataType::CreateString(), vals, vals,
    3676          14 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
    3677             : }
    3678             : 
    3679             : /************************************************************************/
    3680             : /*                                Write()                               */
    3681             : /************************************************************************/
    3682             : 
    3683             : /** Write an attribute from an array of int.
    3684             :  *
    3685             :  * Type conversion will be performed if needed.
    3686             :  *
    3687             :  * Exactly GetTotalElementsCount() strings must be provided
    3688             :  *
    3689             :  * This is the same as the C function GDALAttributeWriteIntArray()
    3690             :  *
    3691             :  * @param vals Array of int.
    3692             :  * @param nVals Should be equal to GetTotalElementsCount().
    3693             :  * @return true in case of success.
    3694             :  */
    3695           9 : bool GDALAttribute::Write(const int *vals, size_t nVals)
    3696             : {
    3697           9 :     if (nVals != GetTotalElementsCount())
    3698             :     {
    3699           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3700           1 :         return false;
    3701             :     }
    3702           8 :     const auto nDims = GetDimensionCount();
    3703          16 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3704           8 :     std::vector<size_t> count(1 + nDims);
    3705           8 :     const auto &dims = GetDimensions();
    3706          16 :     for (size_t i = 0; i < nDims; i++)
    3707           8 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3708           8 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3709           8 :                  GDALExtendedDataType::Create(GDT_Int32), vals, vals,
    3710          16 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
    3711             : }
    3712             : 
    3713             : /************************************************************************/
    3714             : /*                                Write()                               */
    3715             : /************************************************************************/
    3716             : 
    3717             : /** Write an attribute from an array of int64_t.
    3718             :  *
    3719             :  * Type conversion will be performed if needed.
    3720             :  *
    3721             :  * Exactly GetTotalElementsCount() strings must be provided
    3722             :  *
    3723             :  * This is the same as the C function GDALAttributeWriteLongArray()
    3724             :  *
    3725             :  * @param vals Array of int64_t.
    3726             :  * @param nVals Should be equal to GetTotalElementsCount().
    3727             :  * @return true in case of success.
    3728             :  */
    3729          10 : bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
    3730             : {
    3731          10 :     if (nVals != GetTotalElementsCount())
    3732             :     {
    3733           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3734           0 :         return false;
    3735             :     }
    3736          10 :     const auto nDims = GetDimensionCount();
    3737          20 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3738          10 :     std::vector<size_t> count(1 + nDims);
    3739          10 :     const auto &dims = GetDimensions();
    3740          20 :     for (size_t i = 0; i < nDims; i++)
    3741          10 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3742          10 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3743          10 :                  GDALExtendedDataType::Create(GDT_Int64), vals, vals,
    3744          10 :                  static_cast<size_t>(GetTotalElementsCount()) *
    3745          10 :                      sizeof(int64_t));
    3746             : }
    3747             : 
    3748             : /************************************************************************/
    3749             : /*                                Write()                               */
    3750             : /************************************************************************/
    3751             : 
    3752             : /** Write an attribute from an array of double.
    3753             :  *
    3754             :  * Type conversion will be performed if needed.
    3755             :  *
    3756             :  * Exactly GetTotalElementsCount() strings must be provided
    3757             :  *
    3758             :  * This is the same as the C function GDALAttributeWriteDoubleArray()
    3759             :  *
    3760             :  * @param vals Array of double.
    3761             :  * @param nVals Should be equal to GetTotalElementsCount().
    3762             :  * @return true in case of success.
    3763             :  */
    3764           7 : bool GDALAttribute::Write(const double *vals, size_t nVals)
    3765             : {
    3766           7 :     if (nVals != GetTotalElementsCount())
    3767             :     {
    3768           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3769           1 :         return false;
    3770             :     }
    3771           6 :     const auto nDims = GetDimensionCount();
    3772          12 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3773           6 :     std::vector<size_t> count(1 + nDims);
    3774           6 :     const auto &dims = GetDimensions();
    3775          13 :     for (size_t i = 0; i < nDims; i++)
    3776           7 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3777           6 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3778           6 :                  GDALExtendedDataType::Create(GDT_Float64), vals, vals,
    3779          12 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
    3780             : }
    3781             : 
    3782             : /************************************************************************/
    3783             : /*                           GDALMDArray()                              */
    3784             : /************************************************************************/
    3785             : 
    3786             : //! @cond Doxygen_Suppress
    3787        6199 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
    3788             :                          CPL_UNUSED const std::string &osName,
    3789           0 :                          const std::string &osContext)
    3790             :     :
    3791             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
    3792             :       GDALAbstractMDArray(osParentName, osName),
    3793             : #endif
    3794        6199 :       m_osContext(osContext)
    3795             : {
    3796        6199 : }
    3797             : 
    3798             : //! @endcond
    3799             : 
    3800             : /************************************************************************/
    3801             : /*                           GetTotalCopyCost()                         */
    3802             : /************************************************************************/
    3803             : 
    3804             : /** Return a total "cost" to copy the array.
    3805             :  *
    3806             :  * Used as a parameter for CopyFrom()
    3807             :  */
    3808          43 : GUInt64 GDALMDArray::GetTotalCopyCost() const
    3809             : {
    3810          86 :     return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
    3811          86 :            GetTotalElementsCount() * GetDataType().GetSize();
    3812             : }
    3813             : 
    3814             : /************************************************************************/
    3815             : /*                       CopyFromAllExceptValues()                      */
    3816             : /************************************************************************/
    3817             : 
    3818             : //! @cond Doxygen_Suppress
    3819             : 
    3820         144 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
    3821             :                                           bool bStrict, GUInt64 &nCurCost,
    3822             :                                           const GUInt64 nTotalCost,
    3823             :                                           GDALProgressFunc pfnProgress,
    3824             :                                           void *pProgressData)
    3825             : {
    3826             :     // Nodata setting must be one of the first things done for TileDB
    3827         144 :     const void *pNoData = poSrcArray->GetRawNoDataValue();
    3828         144 :     if (pNoData && poSrcArray->GetDataType() == GetDataType())
    3829             :     {
    3830          13 :         SetRawNoDataValue(pNoData);
    3831             :     }
    3832             : 
    3833         144 :     const bool bThisIsUnscaledArray =
    3834         144 :         dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
    3835         288 :     auto attrs = poSrcArray->GetAttributes();
    3836         191 :     for (const auto &attr : attrs)
    3837             :     {
    3838          47 :         const auto &osAttrName = attr->GetName();
    3839          47 :         if (bThisIsUnscaledArray)
    3840             :         {
    3841           6 :             if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
    3842           7 :                 osAttrName == "valid_min" || osAttrName == "valid_max" ||
    3843           1 :                 osAttrName == "valid_range")
    3844             :             {
    3845           1 :                 continue;
    3846             :             }
    3847             :         }
    3848             : 
    3849          46 :         auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
    3850          92 :                                        attr->GetDataType());
    3851          46 :         if (!dstAttr)
    3852             :         {
    3853           0 :             if (bStrict)
    3854           0 :                 return false;
    3855           0 :             continue;
    3856             :         }
    3857          46 :         auto raw = attr->ReadAsRaw();
    3858          46 :         if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
    3859           0 :             return false;
    3860             :     }
    3861         144 :     if (!attrs.empty())
    3862             :     {
    3863          26 :         nCurCost += attrs.size() * GDALAttribute::COPY_COST;
    3864          46 :         if (pfnProgress &&
    3865          20 :             !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    3866           0 :             return false;
    3867             :     }
    3868             : 
    3869         144 :     auto srcSRS = poSrcArray->GetSpatialRef();
    3870         144 :     if (srcSRS)
    3871             :     {
    3872          11 :         SetSpatialRef(srcSRS.get());
    3873             :     }
    3874             : 
    3875         144 :     const std::string &osUnit(poSrcArray->GetUnit());
    3876         144 :     if (!osUnit.empty())
    3877             :     {
    3878          18 :         SetUnit(osUnit);
    3879             :     }
    3880             : 
    3881         144 :     bool bGotValue = false;
    3882         144 :     GDALDataType eOffsetStorageType = GDT_Unknown;
    3883             :     const double dfOffset =
    3884         144 :         poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
    3885         144 :     if (bGotValue)
    3886             :     {
    3887           3 :         SetOffset(dfOffset, eOffsetStorageType);
    3888             :     }
    3889             : 
    3890         144 :     bGotValue = false;
    3891         144 :     GDALDataType eScaleStorageType = GDT_Unknown;
    3892         144 :     const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
    3893         144 :     if (bGotValue)
    3894             :     {
    3895           3 :         SetScale(dfScale, eScaleStorageType);
    3896             :     }
    3897             : 
    3898         144 :     return true;
    3899             : }
    3900             : 
    3901             : //! @endcond
    3902             : 
    3903             : /************************************************************************/
    3904             : /*                               CopyFrom()                             */
    3905             : /************************************************************************/
    3906             : 
    3907             : /** Copy the content of an array into a new (generally empty) array.
    3908             :  *
    3909             :  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
    3910             :  *                   of some output drivers this is not recommended)
    3911             :  * @param poSrcArray Source array. Should NOT be nullptr.
    3912             :  * @param bStrict Whether to enable strict mode. In strict mode, any error will
    3913             :  *                stop the copy. In relaxed mode, the copy will be attempted to
    3914             :  *                be pursued.
    3915             :  * @param nCurCost  Should be provided as a variable initially set to 0.
    3916             :  * @param nTotalCost Total cost from GetTotalCopyCost().
    3917             :  * @param pfnProgress Progress callback, or nullptr.
    3918             :  * @param pProgressData Progress user data, or nulptr.
    3919             :  *
    3920             :  * @return true in case of success (or partial success if bStrict == false).
    3921             :  */
    3922          41 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
    3923             :                            const GDALMDArray *poSrcArray, bool bStrict,
    3924             :                            GUInt64 &nCurCost, const GUInt64 nTotalCost,
    3925             :                            GDALProgressFunc pfnProgress, void *pProgressData)
    3926             : {
    3927          41 :     if (pfnProgress == nullptr)
    3928           4 :         pfnProgress = GDALDummyProgress;
    3929             : 
    3930          41 :     nCurCost += GDALMDArray::COPY_COST;
    3931             : 
    3932          41 :     if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
    3933             :                                  pfnProgress, pProgressData))
    3934             :     {
    3935           0 :         return false;
    3936             :     }
    3937             : 
    3938          41 :     const auto &dims = poSrcArray->GetDimensions();
    3939          41 :     const auto nDTSize = poSrcArray->GetDataType().GetSize();
    3940          41 :     if (dims.empty())
    3941             :     {
    3942           2 :         std::vector<GByte> abyTmp(nDTSize);
    3943           2 :         if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
    3944           2 :                                GetDataType(), &abyTmp[0]) &&
    3945           2 :               Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
    3946           4 :                     &abyTmp[0])) &&
    3947             :             bStrict)
    3948             :         {
    3949           0 :             return false;
    3950             :         }
    3951           2 :         nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
    3952           2 :         if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    3953           0 :             return false;
    3954             :     }
    3955             :     else
    3956             :     {
    3957          39 :         std::vector<GUInt64> arrayStartIdx(dims.size());
    3958          39 :         std::vector<GUInt64> count(dims.size());
    3959         106 :         for (size_t i = 0; i < dims.size(); i++)
    3960             :         {
    3961          67 :             count[i] = static_cast<size_t>(dims[i]->GetSize());
    3962             :         }
    3963             : 
    3964             :         struct CopyFunc
    3965             :         {
    3966             :             GDALMDArray *poDstArray = nullptr;
    3967             :             std::vector<GByte> abyTmp{};
    3968             :             GDALProgressFunc pfnProgress = nullptr;
    3969             :             void *pProgressData = nullptr;
    3970             :             GUInt64 nCurCost = 0;
    3971             :             GUInt64 nTotalCost = 0;
    3972             :             GUInt64 nTotalBytesThisArray = 0;
    3973             :             bool bStop = false;
    3974             : 
    3975          57 :             static bool f(GDALAbstractMDArray *l_poSrcArray,
    3976             :                           const GUInt64 *chunkArrayStartIdx,
    3977             :                           const size_t *chunkCount, GUInt64 iCurChunk,
    3978             :                           GUInt64 nChunkCount, void *pUserData)
    3979             :             {
    3980          57 :                 const auto &dt(l_poSrcArray->GetDataType());
    3981          57 :                 auto data = static_cast<CopyFunc *>(pUserData);
    3982          57 :                 auto poDstArray = data->poDstArray;
    3983          57 :                 if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
    3984          57 :                                         nullptr, dt, &data->abyTmp[0]))
    3985             :                 {
    3986           0 :                     return false;
    3987             :                 }
    3988             :                 bool bRet =
    3989          57 :                     poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
    3990          57 :                                       nullptr, dt, &data->abyTmp[0]);
    3991          57 :                 if (dt.NeedsFreeDynamicMemory())
    3992             :                 {
    3993           2 :                     const auto l_nDTSize = dt.GetSize();
    3994           2 :                     GByte *ptr = &data->abyTmp[0];
    3995           2 :                     const size_t l_nDims(l_poSrcArray->GetDimensionCount());
    3996           2 :                     size_t nEltCount = 1;
    3997           4 :                     for (size_t i = 0; i < l_nDims; ++i)
    3998             :                     {
    3999           2 :                         nEltCount *= chunkCount[i];
    4000             :                     }
    4001          10 :                     for (size_t i = 0; i < nEltCount; i++)
    4002             :                     {
    4003           8 :                         dt.FreeDynamicMemory(ptr);
    4004           8 :                         ptr += l_nDTSize;
    4005             :                     }
    4006             :                 }
    4007          57 :                 if (!bRet)
    4008             :                 {
    4009           0 :                     return false;
    4010             :                 }
    4011             : 
    4012          57 :                 double dfCurCost =
    4013          57 :                     double(data->nCurCost) + double(iCurChunk) / nChunkCount *
    4014          57 :                                                  data->nTotalBytesThisArray;
    4015          57 :                 if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
    4016             :                                        data->pProgressData))
    4017             :                 {
    4018           0 :                     data->bStop = true;
    4019           0 :                     return false;
    4020             :                 }
    4021             : 
    4022          57 :                 return true;
    4023             :             }
    4024             :         };
    4025             : 
    4026          39 :         CopyFunc copyFunc;
    4027          39 :         copyFunc.poDstArray = this;
    4028          39 :         copyFunc.nCurCost = nCurCost;
    4029          39 :         copyFunc.nTotalCost = nTotalCost;
    4030          39 :         copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
    4031          39 :         copyFunc.pfnProgress = pfnProgress;
    4032          39 :         copyFunc.pProgressData = pProgressData;
    4033             :         const char *pszSwathSize =
    4034          39 :             CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
    4035             :         const size_t nMaxChunkSize =
    4036             :             pszSwathSize
    4037          39 :                 ? static_cast<size_t>(
    4038           1 :                       std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    4039           1 :                                CPLAtoGIntBig(pszSwathSize)))
    4040             :                 : static_cast<size_t>(
    4041          38 :                       std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    4042          38 :                                GDALGetCacheMax64() / 4));
    4043          39 :         const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
    4044          39 :         size_t nRealChunkSize = nDTSize;
    4045         106 :         for (const auto &nChunkSize : anChunkSizes)
    4046             :         {
    4047          67 :             nRealChunkSize *= nChunkSize;
    4048             :         }
    4049             :         try
    4050             :         {
    4051          39 :             copyFunc.abyTmp.resize(nRealChunkSize);
    4052             :         }
    4053           0 :         catch (const std::exception &)
    4054             :         {
    4055           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    4056             :                      "Cannot allocate temporary buffer");
    4057           0 :             nCurCost += copyFunc.nTotalBytesThisArray;
    4058           0 :             return false;
    4059             :         }
    4060         116 :         if (copyFunc.nTotalBytesThisArray != 0 &&
    4061          38 :             !const_cast<GDALMDArray *>(poSrcArray)
    4062          38 :                  ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
    4063             :                                    anChunkSizes.data(), CopyFunc::f,
    4064          77 :                                    &copyFunc) &&
    4065           0 :             (bStrict || copyFunc.bStop))
    4066             :         {
    4067           0 :             nCurCost += copyFunc.nTotalBytesThisArray;
    4068           0 :             return false;
    4069             :         }
    4070          39 :         nCurCost += copyFunc.nTotalBytesThisArray;
    4071             :     }
    4072             : 
    4073          41 :     return true;
    4074             : }
    4075             : 
    4076             : /************************************************************************/
    4077             : /*                         GetStructuralInfo()                          */
    4078             : /************************************************************************/
    4079             : 
    4080             : /** Return structural information on the array.
    4081             :  *
    4082             :  * This may be the compression, etc..
    4083             :  *
    4084             :  * The return value should not be freed and is valid until GDALMDArray is
    4085             :  * released or this function called again.
    4086             :  *
    4087             :  * This is the same as the C function GDALMDArrayGetStructuralInfo().
    4088             :  */
    4089          55 : CSLConstList GDALMDArray::GetStructuralInfo() const
    4090             : {
    4091          55 :     return nullptr;
    4092             : }
    4093             : 
    4094             : /************************************************************************/
    4095             : /*                          AdviseRead()                                */
    4096             : /************************************************************************/
    4097             : 
    4098             : /** Advise driver of upcoming read requests.
    4099             :  *
    4100             :  * Some GDAL drivers operate more efficiently if they know in advance what
    4101             :  * set of upcoming read requests will be made.  The AdviseRead() method allows
    4102             :  * an application to notify the driver of the region of interest.
    4103             :  *
    4104             :  * Many drivers just ignore the AdviseRead() call, but it can dramatically
    4105             :  * accelerate access via some drivers. One such case is when reading through
    4106             :  * a DAP dataset with the netCDF driver (a in-memory cache array is then created
    4107             :  * with the region of interest defined by AdviseRead())
    4108             :  *
    4109             :  * This is the same as the C function GDALMDArrayAdviseRead().
    4110             :  *
    4111             :  * @param arrayStartIdx Values representing the starting index to read
    4112             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    4113             :  *                      Array of GetDimensionCount() values.
    4114             :  *                      Can be nullptr as a synonymous for [0 for i in
    4115             :  * range(GetDimensionCount() ]
    4116             :  *
    4117             :  * @param count         Values representing the number of values to extract in
    4118             :  *                      each dimension.
    4119             :  *                      Array of GetDimensionCount() values.
    4120             :  *                      Can be nullptr as a synonymous for
    4121             :  *                      [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
    4122             :  * range(GetDimensionCount() ]
    4123             :  *
    4124             :  * @param papszOptions Driver specific options, or nullptr. Consult driver
    4125             :  * documentation.
    4126             :  *
    4127             :  * @return true in case of success (ignoring the advice is a success)
    4128             :  *
    4129             :  * @since GDAL 3.2
    4130             :  */
    4131          25 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    4132             :                              CSLConstList papszOptions) const
    4133             : {
    4134          25 :     const auto nDimCount = GetDimensionCount();
    4135          25 :     if (nDimCount == 0)
    4136           2 :         return true;
    4137             : 
    4138          46 :     std::vector<GUInt64> tmp_arrayStartIdx;
    4139          23 :     if (arrayStartIdx == nullptr)
    4140             :     {
    4141           0 :         tmp_arrayStartIdx.resize(nDimCount);
    4142           0 :         arrayStartIdx = tmp_arrayStartIdx.data();
    4143             :     }
    4144             : 
    4145          46 :     std::vector<size_t> tmp_count;
    4146          23 :     if (count == nullptr)
    4147             :     {
    4148           0 :         tmp_count.resize(nDimCount);
    4149           0 :         const auto &dims = GetDimensions();
    4150           0 :         for (size_t i = 0; i < nDimCount; i++)
    4151             :         {
    4152           0 :             const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
    4153             : #if SIZEOF_VOIDP < 8
    4154             :             if (nSize != static_cast<size_t>(nSize))
    4155             :             {
    4156             :                 CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
    4157             :                 return false;
    4158             :             }
    4159             : #endif
    4160           0 :             tmp_count[i] = static_cast<size_t>(nSize);
    4161             :         }
    4162           0 :         count = tmp_count.data();
    4163             :     }
    4164             : 
    4165          46 :     std::vector<GInt64> tmp_arrayStep;
    4166          46 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    4167          23 :     const GInt64 *arrayStep = nullptr;
    4168          23 :     const GPtrDiff_t *bufferStride = nullptr;
    4169          23 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    4170          46 :                               GDALExtendedDataType::Create(GDT_Unknown),
    4171             :                               nullptr, nullptr, 0, tmp_arrayStep,
    4172             :                               tmp_bufferStride))
    4173             :     {
    4174           1 :         return false;
    4175             :     }
    4176             : 
    4177          22 :     return IAdviseRead(arrayStartIdx, count, papszOptions);
    4178             : }
    4179             : 
    4180             : /************************************************************************/
    4181             : /*                             IAdviseRead()                            */
    4182             : /************************************************************************/
    4183             : 
    4184             : //! @cond Doxygen_Suppress
    4185           3 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
    4186             :                               CSLConstList /* papszOptions*/) const
    4187             : {
    4188           3 :     return true;
    4189             : }
    4190             : 
    4191             : //! @endcond
    4192             : 
    4193             : /************************************************************************/
    4194             : /*                            MassageName()                             */
    4195             : /************************************************************************/
    4196             : 
    4197             : //! @cond Doxygen_Suppress
    4198          32 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
    4199             : {
    4200          32 :     std::string ret;
    4201         604 :     for (const char ch : inputName)
    4202             :     {
    4203         572 :         if (!isalnum(static_cast<unsigned char>(ch)))
    4204         138 :             ret += '_';
    4205             :         else
    4206         434 :             ret += ch;
    4207             :     }
    4208          32 :     return ret;
    4209             : }
    4210             : 
    4211             : //! @endcond
    4212             : 
    4213             : /************************************************************************/
    4214             : /*                         GetCacheRootGroup()                          */
    4215             : /************************************************************************/
    4216             : 
    4217             : //! @cond Doxygen_Suppress
    4218             : std::shared_ptr<GDALGroup>
    4219        1345 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
    4220             :                                std::string &osCacheFilenameOut) const
    4221             : {
    4222        1345 :     const auto &osFilename = GetFilename();
    4223        1345 :     if (osFilename.empty())
    4224             :     {
    4225           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4226             :                  "Cannot cache an array with an empty filename");
    4227           1 :         return nullptr;
    4228             :     }
    4229             : 
    4230        1344 :     osCacheFilenameOut = osFilename + ".gmac";
    4231        1344 :     if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
    4232             :     {
    4233           0 :         const auto nPosQuestionMark = osFilename.find('?');
    4234           0 :         if (nPosQuestionMark != std::string::npos)
    4235             :         {
    4236             :             osCacheFilenameOut =
    4237           0 :                 osFilename.substr(0, nPosQuestionMark)
    4238           0 :                     .append(".gmac")
    4239           0 :                     .append(osFilename.substr(nPosQuestionMark));
    4240             :         }
    4241             :     }
    4242        1344 :     const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
    4243        1344 :     if (pszProxy != nullptr)
    4244           7 :         osCacheFilenameOut = pszProxy;
    4245             : 
    4246        1344 :     std::unique_ptr<GDALDataset> poDS;
    4247             :     VSIStatBufL sStat;
    4248        1344 :     if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
    4249             :     {
    4250          28 :         poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
    4251             :                                      GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
    4252             :                                      nullptr, nullptr, nullptr));
    4253             :     }
    4254        1344 :     if (poDS)
    4255             :     {
    4256          28 :         CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
    4257          28 :         return poDS->GetRootGroup();
    4258             :     }
    4259             : 
    4260        1316 :     if (bCanCreate)
    4261             :     {
    4262           4 :         const char *pszDrvName = "netCDF";
    4263           4 :         GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
    4264           4 :         if (poDrv == nullptr)
    4265             :         {
    4266           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
    4267             :                      pszDrvName);
    4268           0 :             return nullptr;
    4269             :         }
    4270             :         {
    4271           8 :             CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
    4272           8 :             CPLErrorStateBackuper oErrorStateBackuper;
    4273           4 :             poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
    4274             :                                                      nullptr, nullptr));
    4275             :         }
    4276           4 :         if (!poDS)
    4277             :         {
    4278           1 :             pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
    4279           1 :             if (pszProxy)
    4280             :             {
    4281           1 :                 osCacheFilenameOut = pszProxy;
    4282           1 :                 poDS.reset(poDrv->CreateMultiDimensional(
    4283             :                     osCacheFilenameOut.c_str(), nullptr, nullptr));
    4284             :             }
    4285             :         }
    4286           4 :         if (poDS)
    4287             :         {
    4288           4 :             CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
    4289           4 :             return poDS->GetRootGroup();
    4290             :         }
    4291             :         else
    4292             :         {
    4293           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4294             :                      "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
    4295             :                      "configuration option to write the cache in "
    4296             :                      "another directory",
    4297             :                      osCacheFilenameOut.c_str());
    4298             :         }
    4299             :     }
    4300             : 
    4301        1312 :     return nullptr;
    4302             : }
    4303             : 
    4304             : //! @endcond
    4305             : 
    4306             : /************************************************************************/
    4307             : /*                              Cache()                                 */
    4308             : /************************************************************************/
    4309             : 
    4310             : /** Cache the content of the array into an auxiliary filename.
    4311             :  *
    4312             :  * The main purpose of this method is to be able to cache views that are
    4313             :  * expensive to compute, such as transposed arrays.
    4314             :  *
    4315             :  * The array will be stored in a file whose name is the one of
    4316             :  * GetFilename(), with an extra .gmac extension (stands for GDAL
    4317             :  * Multidimensional Array Cache). The cache is a netCDF dataset.
    4318             :  *
    4319             :  * If the .gmac file cannot be written next to the dataset, the
    4320             :  * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
    4321             :  * directory.
    4322             :  *
    4323             :  * The GDALMDArray::Read() method will automatically use the cache when it
    4324             :  * exists. There is no timestamp checks between the source array and the cached
    4325             :  * array. If the source arrays changes, the cache must be manually deleted.
    4326             :  *
    4327             :  * This is the same as the C function GDALMDArrayCache()
    4328             :  *
    4329             :  * @note Driver implementation: optionally implemented.
    4330             :  *
    4331             :  * @param papszOptions List of options, null terminated, or NULL. Currently
    4332             :  *                     the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
    4333             :  *                     to specify the block size of the cached array.
    4334             :  * @return true in case of success.
    4335             :  */
    4336           7 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
    4337             : {
    4338          14 :     std::string osCacheFilename;
    4339          14 :     auto poRG = GetCacheRootGroup(true, osCacheFilename);
    4340           7 :     if (!poRG)
    4341           1 :         return false;
    4342             : 
    4343          12 :     const std::string osCachedArrayName(MassageName(GetFullName()));
    4344           6 :     if (poRG->OpenMDArray(osCachedArrayName))
    4345             :     {
    4346           2 :         CPLError(CE_Failure, CPLE_NotSupported,
    4347             :                  "An array with same name %s already exists in %s",
    4348             :                  osCachedArrayName.c_str(), osCacheFilename.c_str());
    4349           2 :         return false;
    4350             :     }
    4351             : 
    4352           8 :     CPLStringList aosOptions;
    4353           4 :     aosOptions.SetNameValue("COMPRESS", "DEFLATE");
    4354           4 :     const auto &aoDims = GetDimensions();
    4355           8 :     std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
    4356           4 :     if (!aoDims.empty())
    4357             :     {
    4358             :         std::string osBlockSize(
    4359           4 :             CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
    4360           4 :         if (osBlockSize.empty())
    4361             :         {
    4362           6 :             const auto anBlockSize = GetBlockSize();
    4363           3 :             int idxDim = 0;
    4364          10 :             for (auto nBlockSize : anBlockSize)
    4365             :             {
    4366           7 :                 if (idxDim > 0)
    4367           4 :                     osBlockSize += ',';
    4368           7 :                 if (nBlockSize == 0)
    4369           7 :                     nBlockSize = 256;
    4370           7 :                 nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
    4371             :                 osBlockSize +=
    4372           7 :                     std::to_string(static_cast<uint64_t>(nBlockSize));
    4373           7 :                 idxDim++;
    4374             :             }
    4375             :         }
    4376           4 :         aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
    4377             : 
    4378           4 :         int idxDim = 0;
    4379          13 :         for (const auto &poDim : aoDims)
    4380             :         {
    4381           9 :             auto poNewDim = poRG->CreateDimension(
    4382          18 :                 osCachedArrayName + '_' + std::to_string(idxDim),
    4383          18 :                 poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
    4384           9 :             if (!poNewDim)
    4385           0 :                 return false;
    4386           9 :             aoNewDims.emplace_back(poNewDim);
    4387           9 :             idxDim++;
    4388             :         }
    4389             :     }
    4390             : 
    4391           4 :     auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
    4392           8 :                                              GetDataType(), aosOptions.List());
    4393           4 :     if (!poCachedArray)
    4394             :     {
    4395           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
    4396             :                  osCachedArrayName.c_str(), osCacheFilename.c_str());
    4397           0 :         return false;
    4398             :     }
    4399             : 
    4400           4 :     GUInt64 nCost = 0;
    4401           8 :     return poCachedArray->CopyFrom(nullptr, this,
    4402             :                                    false,  // strict
    4403           4 :                                    nCost, GetTotalCopyCost(), nullptr, nullptr);
    4404             : }
    4405             : 
    4406             : /************************************************************************/
    4407             : /*                               Read()                                 */
    4408             : /************************************************************************/
    4409             : 
    4410        3705 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
    4411             :                        const GInt64 *arrayStep,         // step in elements
    4412             :                        const GPtrDiff_t *bufferStride,  // stride in elements
    4413             :                        const GDALExtendedDataType &bufferDataType,
    4414             :                        void *pDstBuffer, const void *pDstBufferAllocStart,
    4415             :                        size_t nDstBufferAllocSize) const
    4416             : {
    4417        3705 :     if (!m_bHasTriedCachedArray)
    4418             :     {
    4419        1629 :         m_bHasTriedCachedArray = true;
    4420        1629 :         if (IsCacheable())
    4421             :         {
    4422        1629 :             const auto &osFilename = GetFilename();
    4423        2771 :             if (!osFilename.empty() &&
    4424        1142 :                 !EQUAL(CPLGetExtension(osFilename.c_str()), "gmac"))
    4425             :             {
    4426        2264 :                 std::string osCacheFilename;
    4427        2264 :                 auto poRG = GetCacheRootGroup(false, osCacheFilename);
    4428        1132 :                 if (poRG)
    4429             :                 {
    4430             :                     const std::string osCachedArrayName(
    4431          32 :                         MassageName(GetFullName()));
    4432          16 :                     m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
    4433          16 :                     if (m_poCachedArray)
    4434             :                     {
    4435           6 :                         const auto &dims = GetDimensions();
    4436             :                         const auto &cachedDims =
    4437           6 :                             m_poCachedArray->GetDimensions();
    4438           6 :                         const size_t nDims = dims.size();
    4439             :                         bool ok =
    4440          12 :                             m_poCachedArray->GetDataType() == GetDataType() &&
    4441           6 :                             cachedDims.size() == nDims;
    4442          19 :                         for (size_t i = 0; ok && i < nDims; ++i)
    4443             :                         {
    4444          13 :                             ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
    4445             :                         }
    4446           6 :                         if (ok)
    4447             :                         {
    4448           6 :                             CPLDebug("GDAL", "Cached array for %s found in %s",
    4449             :                                      osCachedArrayName.c_str(),
    4450             :                                      osCacheFilename.c_str());
    4451             :                         }
    4452             :                         else
    4453             :                         {
    4454           0 :                             CPLError(CE_Warning, CPLE_AppDefined,
    4455             :                                      "Cached array %s in %s has incompatible "
    4456             :                                      "characteristics with current array.",
    4457             :                                      osCachedArrayName.c_str(),
    4458             :                                      osCacheFilename.c_str());
    4459           0 :                             m_poCachedArray.reset();
    4460             :                         }
    4461             :                     }
    4462             :                 }
    4463             :             }
    4464             :         }
    4465             :     }
    4466             : 
    4467        3705 :     const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
    4468        3705 :     if (!array->GetDataType().CanConvertTo(bufferDataType))
    4469             :     {
    4470           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4471             :                  "Array data type is not convertible to buffer data type");
    4472           0 :         return false;
    4473             :     }
    4474             : 
    4475        7410 :     std::vector<GInt64> tmp_arrayStep;
    4476        7410 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    4477        3705 :     if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
    4478             :                                      bufferStride, bufferDataType, pDstBuffer,
    4479             :                                      pDstBufferAllocStart, nDstBufferAllocSize,
    4480             :                                      tmp_arrayStep, tmp_bufferStride))
    4481             :     {
    4482           9 :         return false;
    4483             :     }
    4484             : 
    4485        3696 :     return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
    4486        3696 :                         bufferDataType, pDstBuffer);
    4487             : }
    4488             : 
    4489             : /************************************************************************/
    4490             : /*                          GetRootGroup()                              */
    4491             : /************************************************************************/
    4492             : 
    4493             : /** Return the root group to which this arrays belongs too.
    4494             :  *
    4495             :  * Note that arrays may be free standing and some drivers may not implement
    4496             :  * this method, hence nullptr may be returned.
    4497             :  *
    4498             :  * It is used internally by the GetResampled() method to detect if GLT
    4499             :  * orthorectification is available.
    4500             :  *
    4501             :  * @return the root group, or nullptr.
    4502             :  * @since GDAL 3.8
    4503             :  */
    4504           0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
    4505             : {
    4506           0 :     return nullptr;
    4507             : }
    4508             : 
    4509             : //! @cond Doxygen_Suppress
    4510             : 
    4511             : /************************************************************************/
    4512             : /*                       IsTransposedRequest()                          */
    4513             : /************************************************************************/
    4514             : 
    4515         689 : bool GDALMDArray::IsTransposedRequest(
    4516             :     const size_t *count,
    4517             :     const GPtrDiff_t *bufferStride) const  // stride in elements
    4518             : {
    4519             :     /*
    4520             :     For example:
    4521             :     count = [2,3,4]
    4522             :     strides = [12, 4, 1]            (2-1)*12+(3-1)*4+(4-1)*1=23   ==> row major
    4523             :     stride [12, 1, 3]            (2-1)*12+(3-1)*1+(4-1)*3=23   ==>
    4524             :     (axis[0],axis[2],axis[1]) transposition [1, 8, 2]             (2-1)*1+
    4525             :     (3-1)*8+(4-1)*2=23   ==> (axis[2],axis[1],axis[0]) transposition
    4526             :     */
    4527         689 :     const size_t nDims(GetDimensionCount());
    4528         689 :     size_t nCurStrideForRowMajorStrides = 1;
    4529         689 :     bool bRowMajorStrides = true;
    4530         689 :     size_t nElts = 1;
    4531         689 :     size_t nLastIdx = 0;
    4532        1954 :     for (size_t i = nDims; i > 0;)
    4533             :     {
    4534        1265 :         --i;
    4535        1265 :         if (bufferStride[i] < 0)
    4536           0 :             return false;
    4537        1265 :         if (static_cast<size_t>(bufferStride[i]) !=
    4538             :             nCurStrideForRowMajorStrides)
    4539             :         {
    4540         221 :             bRowMajorStrides = false;
    4541             :         }
    4542             :         // Integer overflows have already been checked in CheckReadWriteParams()
    4543        1265 :         nCurStrideForRowMajorStrides *= count[i];
    4544        1265 :         nElts *= count[i];
    4545        1265 :         nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
    4546             :     }
    4547         689 :     if (bRowMajorStrides)
    4548         540 :         return false;
    4549         149 :     return nLastIdx == nElts - 1;
    4550             : }
    4551             : 
    4552             : /************************************************************************/
    4553             : /*                   CopyToFinalBufferSameDataType()                    */
    4554             : /************************************************************************/
    4555             : 
    4556             : template <size_t N>
    4557          60 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
    4558             :                                    size_t nDims, const size_t *count,
    4559             :                                    const GPtrDiff_t *bufferStride)
    4560             : {
    4561         120 :     std::vector<size_t> anStackCount(nDims);
    4562         120 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    4563          60 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
    4564             : #if defined(__GNUC__)
    4565             : #pragma GCC diagnostic push
    4566             : #pragma GCC diagnostic ignored "-Wnull-dereference"
    4567             : #endif
    4568          60 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    4569             : #if defined(__GNUC__)
    4570             : #pragma GCC diagnostic pop
    4571             : #endif
    4572          60 :     size_t iDim = 0;
    4573             : 
    4574         749 : lbl_next_depth:
    4575         749 :     if (iDim == nDims - 1)
    4576             :     {
    4577         661 :         size_t n = count[iDim];
    4578         661 :         GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
    4579         661 :         const auto bufferStrideLastDim = bufferStride[iDim] * N;
    4580       29186 :         while (n > 0)
    4581             :         {
    4582       28525 :             --n;
    4583       28525 :             memcpy(pabyDstBuffer, pabySrcBuffer, N);
    4584       28525 :             pabyDstBuffer += bufferStrideLastDim;
    4585       28525 :             pabySrcBuffer += N;
    4586             :         }
    4587             :     }
    4588             :     else
    4589             :     {
    4590          88 :         anStackCount[iDim] = count[iDim];
    4591             :         while (true)
    4592             :         {
    4593         689 :             ++iDim;
    4594         689 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    4595         689 :             goto lbl_next_depth;
    4596         689 :         lbl_return_to_caller_in_loop:
    4597         689 :             --iDim;
    4598         689 :             --anStackCount[iDim];
    4599         689 :             if (anStackCount[iDim] == 0)
    4600          88 :                 break;
    4601         601 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
    4602             :         }
    4603             :     }
    4604         749 :     if (iDim > 0)
    4605         689 :         goto lbl_return_to_caller_in_loop;
    4606          60 : }
    4607             : 
    4608             : /************************************************************************/
    4609             : /*                        CopyToFinalBuffer()                           */
    4610             : /************************************************************************/
    4611             : 
    4612         129 : static void CopyToFinalBuffer(const void *pSrcBuffer,
    4613             :                               const GDALExtendedDataType &eSrcDataType,
    4614             :                               void *pDstBuffer,
    4615             :                               const GDALExtendedDataType &eDstDataType,
    4616             :                               size_t nDims, const size_t *count,
    4617             :                               const GPtrDiff_t *bufferStride)
    4618             : {
    4619         129 :     const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
    4620             :     // Use specialized implementation for well-known data types when no
    4621             :     // type conversion is needed
    4622         129 :     if (eSrcDataType == eDstDataType)
    4623             :     {
    4624         110 :         if (nSrcDataTypeSize == 1)
    4625             :         {
    4626          41 :             CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
    4627             :                                              count, bufferStride);
    4628          60 :             return;
    4629             :         }
    4630          69 :         else if (nSrcDataTypeSize == 2)
    4631             :         {
    4632           1 :             CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
    4633             :                                              count, bufferStride);
    4634           1 :             return;
    4635             :         }
    4636          68 :         else if (nSrcDataTypeSize == 4)
    4637             :         {
    4638          14 :             CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
    4639             :                                              count, bufferStride);
    4640          14 :             return;
    4641             :         }
    4642          54 :         else if (nSrcDataTypeSize == 8)
    4643             :         {
    4644           4 :             CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
    4645             :                                              count, bufferStride);
    4646           4 :             return;
    4647             :         }
    4648             :     }
    4649             : 
    4650          69 :     const size_t nDstDataTypeSize(eDstDataType.GetSize());
    4651         138 :     std::vector<size_t> anStackCount(nDims);
    4652         138 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    4653          69 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
    4654          69 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    4655          69 :     size_t iDim = 0;
    4656             : 
    4657         338 : lbl_next_depth:
    4658         338 :     if (iDim == nDims - 1)
    4659             :     {
    4660         328 :         GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
    4661         328 :                                          pabyDstBufferStack[iDim], eDstDataType,
    4662         328 :                                          bufferStride[iDim], count[iDim]);
    4663         328 :         pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
    4664             :     }
    4665             :     else
    4666             :     {
    4667          10 :         anStackCount[iDim] = count[iDim];
    4668             :         while (true)
    4669             :         {
    4670         269 :             ++iDim;
    4671         269 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    4672         269 :             goto lbl_next_depth;
    4673         269 :         lbl_return_to_caller_in_loop:
    4674         269 :             --iDim;
    4675         269 :             --anStackCount[iDim];
    4676         269 :             if (anStackCount[iDim] == 0)
    4677          10 :                 break;
    4678         259 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
    4679             :         }
    4680             :     }
    4681         338 :     if (iDim > 0)
    4682         269 :         goto lbl_return_to_caller_in_loop;
    4683             : }
    4684             : 
    4685             : /************************************************************************/
    4686             : /*                          Transpose2D()                               */
    4687             : /************************************************************************/
    4688             : 
    4689             : template <class T>
    4690          39 : static void Transpose2D(T *dst, const T *src, size_t src_height,
    4691             :                         size_t src_width)
    4692             : {
    4693          39 :     constexpr size_t blocksize = 32;
    4694         142 :     for (size_t i = 0; i < src_height; i += blocksize)
    4695             :     {
    4696        1264 :         for (size_t j = 0; j < src_width; j += blocksize)
    4697             :         {
    4698             :             // transpose the block beginning at [i,j]
    4699        1161 :             const size_t max_k = std::min(i + blocksize, src_height);
    4700       36699 :             for (size_t k = i; k < max_k; ++k)
    4701             :             {
    4702       35538 :                 const size_t max_l = std::min(j + blocksize, src_width);
    4703     1135486 :                 for (size_t l = j; l < max_l; ++l)
    4704             :                 {
    4705     1099948 :                     dst[k + l * src_height] = src[l + k * src_width];
    4706             :                 }
    4707             :             }
    4708             :         }
    4709             :     }
    4710          39 : }
    4711             : 
    4712             : /************************************************************************/
    4713             : /*                      TransposeLast2Dims()                            */
    4714             : /************************************************************************/
    4715             : 
    4716          19 : static bool TransposeLast2Dims(void *pDstBuffer,
    4717             :                                const GDALExtendedDataType &eDT,
    4718             :                                const size_t nDims, const size_t *count,
    4719             :                                const size_t nEltsNonLast2Dims)
    4720             : {
    4721          19 :     const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
    4722          19 :     const auto nDTSize = eDT.GetSize();
    4723             :     void *pTempBufferForLast2DimsTranspose =
    4724          19 :         VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
    4725          19 :     if (pTempBufferForLast2DimsTranspose == nullptr)
    4726           0 :         return false;
    4727             : 
    4728          19 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
    4729          58 :     for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
    4730             :     {
    4731          39 :         if (nDTSize == 1)
    4732             :         {
    4733          14 :             Transpose2D(
    4734             :                 static_cast<uint8_t *>(pTempBufferForLast2DimsTranspose),
    4735             :                 reinterpret_cast<const uint8_t *>(pabyDstBuffer),
    4736          14 :                 count[nDims - 2], count[nDims - 1]);
    4737             :         }
    4738          25 :         else if (nDTSize == 2)
    4739             :         {
    4740           8 :             Transpose2D(
    4741             :                 static_cast<uint16_t *>(pTempBufferForLast2DimsTranspose),
    4742             :                 reinterpret_cast<const uint16_t *>(pabyDstBuffer),
    4743           8 :                 count[nDims - 2], count[nDims - 1]);
    4744             :         }
    4745          17 :         else if (nDTSize == 4)
    4746             :         {
    4747           9 :             Transpose2D(
    4748             :                 static_cast<uint32_t *>(pTempBufferForLast2DimsTranspose),
    4749             :                 reinterpret_cast<const uint32_t *>(pabyDstBuffer),
    4750           9 :                 count[nDims - 2], count[nDims - 1]);
    4751             :         }
    4752           8 :         else if (nDTSize == 8)
    4753             :         {
    4754           8 :             Transpose2D(
    4755             :                 static_cast<uint64_t *>(pTempBufferForLast2DimsTranspose),
    4756             :                 reinterpret_cast<const uint64_t *>(pabyDstBuffer),
    4757           8 :                 count[nDims - 2], count[nDims - 1]);
    4758             :         }
    4759             :         else
    4760             :         {
    4761           0 :             CPLAssert(false);
    4762             :         }
    4763          39 :         memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
    4764             :                nDTSize * nEltsLast2Dims);
    4765          39 :         pabyDstBuffer += nDTSize * nEltsLast2Dims;
    4766             :     }
    4767             : 
    4768          19 :     VSIFree(pTempBufferForLast2DimsTranspose);
    4769             : 
    4770          19 :     return true;
    4771             : }
    4772             : 
    4773             : /************************************************************************/
    4774             : /*                      ReadForTransposedRequest()                      */
    4775             : /************************************************************************/
    4776             : 
    4777             : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
    4778             : // transposed view yield to extremely poor/unusable performance. This fixes
    4779             : // this by using temporary memory to read in a contiguous buffer in a
    4780             : // row-major order, and then do the transposition to the final buffer.
    4781             : 
    4782         148 : bool GDALMDArray::ReadForTransposedRequest(
    4783             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    4784             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
    4785             :     void *pDstBuffer) const
    4786             : {
    4787         148 :     const size_t nDims(GetDimensionCount());
    4788         148 :     if (nDims == 0)
    4789             :     {
    4790           0 :         CPLAssert(false);
    4791             :         return false;  // shouldn't happen
    4792             :     }
    4793         148 :     size_t nElts = 1;
    4794         418 :     for (size_t i = 0; i < nDims; ++i)
    4795         270 :         nElts *= count[i];
    4796             : 
    4797         296 :     std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
    4798         148 :     tmpBufferStrides.back() = 1;
    4799         270 :     for (size_t i = nDims - 1; i > 0;)
    4800             :     {
    4801         122 :         --i;
    4802         122 :         tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
    4803             :     }
    4804             : 
    4805         148 :     const auto &eDT = GetDataType();
    4806         148 :     const auto nDTSize = eDT.GetSize();
    4807         277 :     if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
    4808         293 :         static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
    4809          16 :         (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
    4810             :     {
    4811             :         // Optimization of the optimization if only the last 2 dims are
    4812             :         // transposed that saves on temporary buffer allocation
    4813          23 :         const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
    4814          23 :         size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
    4815          23 :         bool bRowMajorStridesForNonLast2Dims = true;
    4816          23 :         size_t nEltsNonLast2Dims = 1;
    4817          40 :         for (size_t i = nDims - 2; i > 0;)
    4818             :         {
    4819          17 :             --i;
    4820          17 :             if (static_cast<size_t>(bufferStride[i]) !=
    4821             :                 nCurStrideForRowMajorStrides)
    4822             :             {
    4823           4 :                 bRowMajorStridesForNonLast2Dims = false;
    4824             :             }
    4825             :             // Integer overflows have already been checked in
    4826             :             // CheckReadWriteParams()
    4827          17 :             nCurStrideForRowMajorStrides *= count[i];
    4828          17 :             nEltsNonLast2Dims *= count[i];
    4829             :         }
    4830          23 :         if (bRowMajorStridesForNonLast2Dims)
    4831             :         {
    4832             :             // We read in the final buffer!
    4833          19 :             if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
    4834          19 :                        eDT, pDstBuffer))
    4835             :             {
    4836           0 :                 return false;
    4837             :             }
    4838             : 
    4839          19 :             return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
    4840          19 :                                       nEltsNonLast2Dims);
    4841             :         }
    4842             :     }
    4843             : 
    4844         129 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
    4845         129 :     if (pTempBuffer == nullptr)
    4846           0 :         return false;
    4847             : 
    4848         129 :     if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
    4849         129 :                pTempBuffer))
    4850             :     {
    4851           0 :         VSIFree(pTempBuffer);
    4852           0 :         return false;
    4853             :     }
    4854         129 :     CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
    4855             :                       count, bufferStride);
    4856             : 
    4857         129 :     if (eDT.NeedsFreeDynamicMemory())
    4858             :     {
    4859          58 :         GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
    4860         116 :         for (size_t i = 0; i < nElts; ++i)
    4861             :         {
    4862          58 :             eDT.FreeDynamicMemory(pabyPtr);
    4863          58 :             pabyPtr += nDTSize;
    4864             :         }
    4865             :     }
    4866             : 
    4867         129 :     VSIFree(pTempBuffer);
    4868         129 :     return true;
    4869             : }
    4870             : 
    4871             : /************************************************************************/
    4872             : /*               IsStepOneContiguousRowMajorOrderedSameDataType()       */
    4873             : /************************************************************************/
    4874             : 
    4875             : // Returns true if at all following conditions are met:
    4876             : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
    4877             : // defines a row-major ordered contiguous buffer.
    4878          78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
    4879             :     const size_t *count, const GInt64 *arrayStep,
    4880             :     const GPtrDiff_t *bufferStride,
    4881             :     const GDALExtendedDataType &bufferDataType) const
    4882             : {
    4883          78 :     if (bufferDataType != GetDataType())
    4884           5 :         return false;
    4885          73 :     size_t nExpectedStride = 1;
    4886         166 :     for (size_t i = GetDimensionCount(); i > 0;)
    4887             :     {
    4888          96 :         --i;
    4889          96 :         if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
    4890          94 :             static_cast<size_t>(bufferStride[i]) != nExpectedStride)
    4891             :         {
    4892           3 :             return false;
    4893             :         }
    4894          93 :         nExpectedStride *= count[i];
    4895             :     }
    4896          70 :     return true;
    4897             : }
    4898             : 
    4899             : /************************************************************************/
    4900             : /*                      ReadUsingContiguousIRead()                      */
    4901             : /************************************************************************/
    4902             : 
    4903             : // Used for example by the TileDB driver when requesting it with
    4904             : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
    4905             : // not defining a row-major ordered contiguous buffer.
    4906             : // Should only be called when at least one of the above conditions are true,
    4907             : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
    4908             : // returning none.
    4909             : // This method will call IRead() again with arrayStep[] == 1,
    4910             : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
    4911             : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
    4912             : // content of that temporary buffer onto pDstBuffer.
    4913           7 : bool GDALMDArray::ReadUsingContiguousIRead(
    4914             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    4915             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
    4916             :     void *pDstBuffer) const
    4917             : {
    4918           7 :     const size_t nDims(GetDimensionCount());
    4919          14 :     std::vector<GUInt64> anTmpStartIdx(nDims);
    4920          14 :     std::vector<size_t> anTmpCount(nDims);
    4921           7 :     const auto &oType = GetDataType();
    4922           7 :     size_t nMemArraySize = oType.GetSize();
    4923          14 :     std::vector<GPtrDiff_t> anTmpStride(nDims);
    4924           7 :     GPtrDiff_t nStride = 1;
    4925          18 :     for (size_t i = nDims; i > 0;)
    4926             :     {
    4927          11 :         --i;
    4928          11 :         if (arrayStep[i] > 0)
    4929           9 :             anTmpStartIdx[i] = arrayStartIdx[i];
    4930             :         else
    4931           2 :             anTmpStartIdx[i] =
    4932           2 :                 arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
    4933             :         const uint64_t nCount =
    4934          11 :             (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
    4935          11 :         if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
    4936             :         {
    4937           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4938             :                      "Read() failed due to too large memory requirement");
    4939           0 :             return false;
    4940             :         }
    4941          11 :         anTmpCount[i] = static_cast<size_t>(nCount);
    4942          11 :         nMemArraySize *= anTmpCount[i];
    4943          11 :         anTmpStride[i] = nStride;
    4944          11 :         nStride *= anTmpCount[i];
    4945             :     }
    4946             :     std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
    4947          14 :         VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
    4948           7 :     if (!pTmpBuffer)
    4949           0 :         return false;
    4950           7 :     if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
    4951          14 :                std::vector<GInt64>(nDims, 1).data(),  // steps
    4952           7 :                anTmpStride.data(), oType, pTmpBuffer.get()))
    4953             :     {
    4954           0 :         return false;
    4955             :     }
    4956          14 :     std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
    4957          18 :     for (size_t i = 0; i < nDims; ++i)
    4958             :     {
    4959          11 :         if (arrayStep[i] > 0)
    4960           9 :             anTmpStartIdx[i] = 0;
    4961             :         else
    4962           2 :             anTmpStartIdx[i] = anTmpCount[i] - 1;
    4963          22 :         apoTmpDims[i] = std::make_shared<GDALDimension>(
    4964          22 :             std::string(), std::string(), std::string(), std::string(),
    4965          22 :             anTmpCount[i]);
    4966             :     }
    4967             :     auto poMEMArray =
    4968          14 :         MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
    4969          21 :     return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
    4970           7 :            poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
    4971           7 :                             bufferStride, bufferDataType, pDstBuffer);
    4972             : }
    4973             : 
    4974             : //! @endcond
    4975             : 
    4976             : /************************************************************************/
    4977             : /*                       GDALSlicedMDArray                              */
    4978             : /************************************************************************/
    4979             : 
    4980             : class GDALSlicedMDArray final : public GDALPamMDArray
    4981             : {
    4982             :   private:
    4983             :     std::shared_ptr<GDALMDArray> m_poParent{};
    4984             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    4985             :     std::vector<size_t> m_mapDimIdxToParentDimIdx{};  // of size m_dims.size()
    4986             :     std::vector<Range>
    4987             :         m_parentRanges{};  // of size m_poParent->GetDimensionCount()
    4988             : 
    4989             :     mutable std::vector<GUInt64> m_parentStart;
    4990             :     mutable std::vector<size_t> m_parentCount;
    4991             :     mutable std::vector<GInt64> m_parentStep;
    4992             :     mutable std::vector<GPtrDiff_t> m_parentStride;
    4993             : 
    4994             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
    4995             :                              const GInt64 *arrayStep,
    4996             :                              const GPtrDiff_t *bufferStride) const;
    4997             : 
    4998             :   protected:
    4999         574 :     explicit GDALSlicedMDArray(
    5000             :         const std::shared_ptr<GDALMDArray> &poParent,
    5001             :         const std::string &viewExpr,
    5002             :         std::vector<std::shared_ptr<GDALDimension>> &&dims,
    5003             :         std::vector<size_t> &&mapDimIdxToParentDimIdx,
    5004             :         std::vector<Range> &&parentRanges)
    5005        1722 :         : GDALAbstractMDArray(std::string(), "Sliced view of " +
    5006        1722 :                                                  poParent->GetFullName() +
    5007        1148 :                                                  " (" + viewExpr + ")"),
    5008        1148 :           GDALPamMDArray(std::string(),
    5009        1148 :                          "Sliced view of " + poParent->GetFullName() + " (" +
    5010        1148 :                              viewExpr + ")",
    5011        1148 :                          GDALPamMultiDim::GetPAM(poParent),
    5012             :                          poParent->GetContext()),
    5013        1148 :           m_poParent(std::move(poParent)), m_dims(std::move(dims)),
    5014         574 :           m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
    5015         574 :           m_parentRanges(std::move(parentRanges)),
    5016         574 :           m_parentStart(m_poParent->GetDimensionCount()),
    5017         574 :           m_parentCount(m_poParent->GetDimensionCount(), 1),
    5018         574 :           m_parentStep(m_poParent->GetDimensionCount()),
    5019        4592 :           m_parentStride(m_poParent->GetDimensionCount())
    5020             :     {
    5021         574 :     }
    5022             : 
    5023             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5024             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5025             :                const GDALExtendedDataType &bufferDataType,
    5026             :                void *pDstBuffer) const override;
    5027             : 
    5028             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    5029             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5030             :                 const GDALExtendedDataType &bufferDataType,
    5031             :                 const void *pSrcBuffer) override;
    5032             : 
    5033             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5034             :                      CSLConstList papszOptions) const override;
    5035             : 
    5036             :   public:
    5037             :     static std::shared_ptr<GDALSlicedMDArray>
    5038         574 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5039             :            const std::string &viewExpr,
    5040             :            std::vector<std::shared_ptr<GDALDimension>> &&dims,
    5041             :            std::vector<size_t> &&mapDimIdxToParentDimIdx,
    5042             :            std::vector<Range> &&parentRanges)
    5043             :     {
    5044         574 :         CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
    5045         574 :         CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
    5046             : 
    5047             :         auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
    5048         574 :             poParent, viewExpr, std::move(dims),
    5049         574 :             std::move(mapDimIdxToParentDimIdx), std::move(parentRanges))));
    5050         574 :         newAr->SetSelf(newAr);
    5051         574 :         return newAr;
    5052             :     }
    5053             : 
    5054          55 :     bool IsWritable() const override
    5055             :     {
    5056          55 :         return m_poParent->IsWritable();
    5057             :     }
    5058             : 
    5059         983 :     const std::string &GetFilename() const override
    5060             :     {
    5061         983 :         return m_poParent->GetFilename();
    5062             :     }
    5063             : 
    5064             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5065        3646 :     GetDimensions() const override
    5066             :     {
    5067        3646 :         return m_dims;
    5068             :     }
    5069             : 
    5070        1383 :     const GDALExtendedDataType &GetDataType() const override
    5071             :     {
    5072        1383 :         return m_poParent->GetDataType();
    5073             :     }
    5074             : 
    5075           2 :     const std::string &GetUnit() const override
    5076             :     {
    5077           2 :         return m_poParent->GetUnit();
    5078             :     }
    5079             : 
    5080             :     // bool SetUnit(const std::string& osUnit) override  { return
    5081             :     // m_poParent->SetUnit(osUnit); }
    5082             : 
    5083           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5084             :     {
    5085           4 :         auto poSrcSRS = m_poParent->GetSpatialRef();
    5086           2 :         if (!poSrcSRS)
    5087           1 :             return nullptr;
    5088           2 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
    5089           2 :         std::vector<int> dstMapping;
    5090           3 :         for (int srcAxis : srcMapping)
    5091             :         {
    5092           2 :             bool bFound = false;
    5093           3 :             for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
    5094             :             {
    5095           3 :                 if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
    5096           3 :                     srcAxis - 1)
    5097             :                 {
    5098           2 :                     dstMapping.push_back(static_cast<int>(i) + 1);
    5099           2 :                     bFound = true;
    5100           2 :                     break;
    5101             :                 }
    5102             :             }
    5103           2 :             if (!bFound)
    5104             :             {
    5105           0 :                 dstMapping.push_back(0);
    5106             :             }
    5107             :         }
    5108           2 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
    5109           1 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
    5110           1 :         return poClone;
    5111             :     }
    5112             : 
    5113          55 :     const void *GetRawNoDataValue() const override
    5114             :     {
    5115          55 :         return m_poParent->GetRawNoDataValue();
    5116             :     }
    5117             : 
    5118             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
    5119             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
    5120             : 
    5121           2 :     double GetOffset(bool *pbHasOffset,
    5122             :                      GDALDataType *peStorageType) const override
    5123             :     {
    5124           2 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5125             :     }
    5126             : 
    5127           2 :     double GetScale(bool *pbHasScale,
    5128             :                     GDALDataType *peStorageType) const override
    5129             :     {
    5130           2 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5131             :     }
    5132             : 
    5133             :     // bool SetOffset(double dfOffset) override { return
    5134             :     // m_poParent->SetOffset(dfOffset); }
    5135             : 
    5136             :     // bool SetScale(double dfScale) override { return
    5137             :     // m_poParent->SetScale(dfScale); }
    5138             : 
    5139         197 :     std::vector<GUInt64> GetBlockSize() const override
    5140             :     {
    5141         197 :         std::vector<GUInt64> ret(GetDimensionCount());
    5142         394 :         const auto parentBlockSize(m_poParent->GetBlockSize());
    5143         595 :         for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
    5144             :         {
    5145         398 :             const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
    5146         398 :             if (iOldAxis != static_cast<size_t>(-1))
    5147             :             {
    5148         398 :                 ret[i] = parentBlockSize[iOldAxis];
    5149             :             }
    5150             :         }
    5151         394 :         return ret;
    5152             :     }
    5153             : 
    5154             :     std::shared_ptr<GDALAttribute>
    5155           6 :     GetAttribute(const std::string &osName) const override
    5156             :     {
    5157           6 :         return m_poParent->GetAttribute(osName);
    5158             :     }
    5159             : 
    5160             :     std::vector<std::shared_ptr<GDALAttribute>>
    5161          24 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    5162             :     {
    5163          24 :         return m_poParent->GetAttributes(papszOptions);
    5164             :     }
    5165             : };
    5166             : 
    5167             : /************************************************************************/
    5168             : /*                        PrepareParentArrays()                         */
    5169             : /************************************************************************/
    5170             : 
    5171         475 : void GDALSlicedMDArray::PrepareParentArrays(
    5172             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    5173             :     const GPtrDiff_t *bufferStride) const
    5174             : {
    5175         475 :     const size_t nParentDimCount = m_parentRanges.size();
    5176        1481 :     for (size_t i = 0; i < nParentDimCount; i++)
    5177             :     {
    5178             :         // For dimensions in parent that have no existence in sliced array
    5179        1006 :         m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
    5180             :     }
    5181             : 
    5182        1250 :     for (size_t i = 0; i < m_dims.size(); i++)
    5183             :     {
    5184         775 :         const auto iParent = m_mapDimIdxToParentDimIdx[i];
    5185         775 :         if (iParent != static_cast<size_t>(-1))
    5186             :         {
    5187         773 :             m_parentStart[iParent] =
    5188         773 :                 m_parentRanges[iParent].m_nIncr >= 0
    5189         773 :                     ? m_parentRanges[iParent].m_nStartIdx +
    5190         744 :                           arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
    5191          29 :                     : m_parentRanges[iParent].m_nStartIdx -
    5192          58 :                           arrayStartIdx[i] *
    5193          29 :                               static_cast<GUInt64>(
    5194          29 :                                   -m_parentRanges[iParent].m_nIncr);
    5195         773 :             m_parentCount[iParent] = count[i];
    5196         773 :             if (arrayStep)
    5197             :             {
    5198         772 :                 m_parentStep[iParent] =
    5199         772 :                     count[i] == 1 ? 1 :
    5200             :                                   // other checks should have ensured this does
    5201             :                         // not overflow
    5202         586 :                         arrayStep[i] * m_parentRanges[iParent].m_nIncr;
    5203             :             }
    5204         773 :             if (bufferStride)
    5205             :             {
    5206         772 :                 m_parentStride[iParent] = bufferStride[i];
    5207             :             }
    5208             :         }
    5209             :     }
    5210         475 : }
    5211             : 
    5212             : /************************************************************************/
    5213             : /*                             IRead()                                  */
    5214             : /************************************************************************/
    5215             : 
    5216         442 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5217             :                               const GInt64 *arrayStep,
    5218             :                               const GPtrDiff_t *bufferStride,
    5219             :                               const GDALExtendedDataType &bufferDataType,
    5220             :                               void *pDstBuffer) const
    5221             : {
    5222         442 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5223         884 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
    5224         442 :                             m_parentStep.data(), m_parentStride.data(),
    5225         442 :                             bufferDataType, pDstBuffer);
    5226             : }
    5227             : 
    5228             : /************************************************************************/
    5229             : /*                             IWrite()                                  */
    5230             : /************************************************************************/
    5231             : 
    5232          32 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
    5233             :                                const size_t *count, const GInt64 *arrayStep,
    5234             :                                const GPtrDiff_t *bufferStride,
    5235             :                                const GDALExtendedDataType &bufferDataType,
    5236             :                                const void *pSrcBuffer)
    5237             : {
    5238          32 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5239          64 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
    5240          32 :                              m_parentStep.data(), m_parentStride.data(),
    5241          32 :                              bufferDataType, pSrcBuffer);
    5242             : }
    5243             : 
    5244             : /************************************************************************/
    5245             : /*                             IAdviseRead()                            */
    5246             : /************************************************************************/
    5247             : 
    5248           1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
    5249             :                                     const size_t *count,
    5250             :                                     CSLConstList papszOptions) const
    5251             : {
    5252           1 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
    5253           1 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
    5254           1 :                                   papszOptions);
    5255             : }
    5256             : 
    5257             : /************************************************************************/
    5258             : /*                        CreateSlicedArray()                           */
    5259             : /************************************************************************/
    5260             : 
    5261             : static std::shared_ptr<GDALMDArray>
    5262         592 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
    5263             :                   const std::string &viewExpr, const std::string &activeSlice,
    5264             :                   bool bRenameDimensions,
    5265             :                   std::vector<GDALMDArray::ViewSpec> &viewSpecs)
    5266             : {
    5267         592 :     const auto &srcDims(self->GetDimensions());
    5268         592 :     if (srcDims.empty())
    5269             :     {
    5270           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
    5271           2 :         return nullptr;
    5272             :     }
    5273             : 
    5274        1180 :     CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
    5275         590 :     const auto nTokens = static_cast<size_t>(aosTokens.size());
    5276             : 
    5277        1180 :     std::vector<std::shared_ptr<GDALDimension>> newDims;
    5278        1180 :     std::vector<size_t> mapDimIdxToParentDimIdx;
    5279        1180 :     std::vector<GDALSlicedMDArray::Range> parentRanges;
    5280         590 :     newDims.reserve(nTokens);
    5281         590 :     mapDimIdxToParentDimIdx.reserve(nTokens);
    5282         590 :     parentRanges.reserve(nTokens);
    5283             : 
    5284         590 :     bool bGotEllipsis = false;
    5285         590 :     size_t nCurSrcDim = 0;
    5286        1744 :     for (size_t i = 0; i < nTokens; i++)
    5287             :     {
    5288        1170 :         const char *pszIdxSpec = aosTokens[i];
    5289        1170 :         if (EQUAL(pszIdxSpec, "..."))
    5290             :         {
    5291          37 :             if (bGotEllipsis)
    5292             :             {
    5293           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5294             :                          "Only one single ellipsis is supported");
    5295           2 :                 return nullptr;
    5296             :             }
    5297          35 :             bGotEllipsis = true;
    5298          35 :             const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
    5299          77 :             for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
    5300             :             {
    5301          42 :                 parentRanges.emplace_back(0, 1);
    5302          42 :                 newDims.push_back(srcDims[nCurSrcDim]);
    5303          42 :                 mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5304             :             }
    5305          35 :             continue;
    5306             :         }
    5307        1133 :         else if (EQUAL(pszIdxSpec, "newaxis") ||
    5308        1130 :                  EQUAL(pszIdxSpec, "np.newaxis"))
    5309             :         {
    5310           3 :             newDims.push_back(std::make_shared<GDALDimension>(
    5311           6 :                 std::string(), "newaxis", std::string(), std::string(), 1));
    5312           3 :             mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
    5313           3 :             continue;
    5314             :         }
    5315        1130 :         else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
    5316             :         {
    5317         323 :             if (nCurSrcDim >= srcDims.size())
    5318             :             {
    5319           2 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
    5320             :                          activeSlice.c_str());
    5321           7 :                 return nullptr;
    5322             :             }
    5323             : 
    5324         321 :             auto nVal = CPLAtoGIntBig(pszIdxSpec);
    5325         321 :             GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
    5326         321 :             if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
    5327         317 :                 (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
    5328             :             {
    5329           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5330             :                          "Index " CPL_FRMT_GIB " is out of bounds", nVal);
    5331           5 :                 return nullptr;
    5332             :             }
    5333         316 :             if (nVal < 0)
    5334           0 :                 nVal += nDimSize;
    5335         316 :             parentRanges.emplace_back(nVal, 0);
    5336             :         }
    5337             :         else
    5338             :         {
    5339         807 :             if (nCurSrcDim >= srcDims.size())
    5340             :             {
    5341           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
    5342             :                          activeSlice.c_str());
    5343           7 :                 return nullptr;
    5344             :             }
    5345             : 
    5346             :             CPLStringList aosRangeTokens(
    5347         806 :                 CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
    5348         806 :             int nRangeTokens = aosRangeTokens.size();
    5349         806 :             if (nRangeTokens > 3)
    5350             :             {
    5351           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
    5352             :                          pszIdxSpec);
    5353           1 :                 return nullptr;
    5354             :             }
    5355         805 :             if (nRangeTokens <= 1)
    5356             :             {
    5357           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
    5358             :                          pszIdxSpec);
    5359           1 :                 return nullptr;
    5360             :             }
    5361         804 :             const char *pszStart = aosRangeTokens[0];
    5362         804 :             const char *pszEnd = aosRangeTokens[1];
    5363         804 :             const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
    5364         804 :             GDALSlicedMDArray::Range range;
    5365         804 :             const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
    5366         804 :             range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
    5367        1607 :             if (range.m_nIncr == 0 ||
    5368         803 :                 range.m_nIncr == std::numeric_limits<GInt64>::min())
    5369             :             {
    5370           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
    5371           1 :                 return nullptr;
    5372             :             }
    5373         803 :             auto startIdx(CPLAtoGIntBig(pszStart));
    5374         803 :             if (startIdx < 0)
    5375             :             {
    5376           0 :                 if (nDimSize < static_cast<GUInt64>(-startIdx))
    5377           0 :                     startIdx = 0;
    5378             :                 else
    5379           0 :                     startIdx = nDimSize + startIdx;
    5380             :             }
    5381         803 :             const bool bPosIncr = range.m_nIncr > 0;
    5382         803 :             range.m_nStartIdx = startIdx;
    5383        1606 :             range.m_nStartIdx = EQUAL(pszStart, "")
    5384         803 :                                     ? (bPosIncr ? 0 : nDimSize - 1)
    5385             :                                     : range.m_nStartIdx;
    5386         803 :             if (range.m_nStartIdx >= nDimSize - 1)
    5387         185 :                 range.m_nStartIdx = nDimSize - 1;
    5388         803 :             auto endIdx(CPLAtoGIntBig(pszEnd));
    5389         803 :             if (endIdx < 0)
    5390             :             {
    5391           1 :                 const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
    5392           1 :                 if (nDimSize < positiveEndIdx)
    5393           0 :                     endIdx = 0;
    5394             :                 else
    5395           1 :                     endIdx = nDimSize - positiveEndIdx;
    5396             :             }
    5397         803 :             GUInt64 nEndIdx = endIdx;
    5398         803 :             nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
    5399         803 :             if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
    5400         801 :                 (!bPosIncr && range.m_nStartIdx <= nEndIdx))
    5401             :             {
    5402           3 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5403             :                          "Output dimension of size 0 is not allowed");
    5404           3 :                 return nullptr;
    5405             :             }
    5406         800 :             int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
    5407         800 :             const auto nAbsIncr = std::abs(range.m_nIncr);
    5408         800 :             const GUInt64 newSize =
    5409             :                 bPosIncr
    5410         833 :                     ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
    5411          33 :                     : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
    5412        1322 :             if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
    5413         522 :                 newSize == srcDims[nCurSrcDim]->GetSize())
    5414             :             {
    5415         153 :                 newDims.push_back(srcDims[nCurSrcDim]);
    5416             :             }
    5417             :             else
    5418             :             {
    5419         647 :                 std::string osNewDimName(srcDims[nCurSrcDim]->GetName());
    5420         647 :                 if (bRenameDimensions)
    5421             :                 {
    5422             :                     osNewDimName =
    5423        1210 :                         "subset_" + srcDims[nCurSrcDim]->GetName() +
    5424             :                         CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
    5425             :                                    "_" CPL_FRMT_GUIB,
    5426         605 :                                    static_cast<GUIntBig>(range.m_nStartIdx),
    5427         605 :                                    static_cast<GIntBig>(range.m_nIncr),
    5428         605 :                                    static_cast<GUIntBig>(newSize));
    5429             :                 }
    5430         647 :                 newDims.push_back(std::make_shared<GDALDimension>(
    5431        1294 :                     std::string(), osNewDimName, srcDims[nCurSrcDim]->GetType(),
    5432        1294 :                     range.m_nIncr > 0 ? srcDims[nCurSrcDim]->GetDirection()
    5433             :                                       : std::string(),
    5434             :                     newSize));
    5435             :             }
    5436         800 :             mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5437         800 :             parentRanges.emplace_back(range);
    5438             :         }
    5439             : 
    5440        1116 :         nCurSrcDim++;
    5441             :     }
    5442         647 :     for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
    5443             :     {
    5444          73 :         parentRanges.emplace_back(0, 1);
    5445          73 :         newDims.push_back(srcDims[nCurSrcDim]);
    5446          73 :         mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5447             :     }
    5448             : 
    5449         574 :     GDALMDArray::ViewSpec viewSpec;
    5450         574 :     viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
    5451         574 :     viewSpec.m_parentRanges = parentRanges;
    5452         574 :     viewSpecs.emplace_back(std::move(viewSpec));
    5453             : 
    5454        1148 :     return GDALSlicedMDArray::Create(self, viewExpr, std::move(newDims),
    5455         574 :                                      std::move(mapDimIdxToParentDimIdx),
    5456        1148 :                                      std::move(parentRanges));
    5457             : }
    5458             : 
    5459             : /************************************************************************/
    5460             : /*                       GDALExtractFieldMDArray                        */
    5461             : /************************************************************************/
    5462             : 
    5463             : class GDALExtractFieldMDArray final : public GDALPamMDArray
    5464             : {
    5465             :   private:
    5466             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5467             :     GDALExtendedDataType m_dt;
    5468             :     std::string m_srcCompName;
    5469             :     mutable std::vector<GByte> m_pabyNoData{};
    5470             : 
    5471             :   protected:
    5472          62 :     GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
    5473             :                             const std::string &fieldName,
    5474             :                             const std::unique_ptr<GDALEDTComponent> &srcComp)
    5475         248 :         : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
    5476         124 :                                                  " of " +
    5477          62 :                                                  poParent->GetFullName()),
    5478             :           GDALPamMDArray(
    5479         124 :               std::string(),
    5480         124 :               "Extract field " + fieldName + " of " + poParent->GetFullName(),
    5481         124 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
    5482             :           m_poParent(poParent), m_dt(srcComp->GetType()),
    5483         310 :           m_srcCompName(srcComp->GetName())
    5484             :     {
    5485          62 :         m_pabyNoData.resize(m_dt.GetSize());
    5486          62 :     }
    5487             : 
    5488             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5489             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5490             :                const GDALExtendedDataType &bufferDataType,
    5491             :                void *pDstBuffer) const override;
    5492             : 
    5493           1 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5494             :                      CSLConstList papszOptions) const override
    5495             :     {
    5496           1 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
    5497             :     }
    5498             : 
    5499             :   public:
    5500             :     static std::shared_ptr<GDALExtractFieldMDArray>
    5501          62 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5502             :            const std::string &fieldName,
    5503             :            const std::unique_ptr<GDALEDTComponent> &srcComp)
    5504             :     {
    5505             :         auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
    5506          62 :             new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
    5507          62 :         newAr->SetSelf(newAr);
    5508          62 :         return newAr;
    5509             :     }
    5510             : 
    5511         124 :     ~GDALExtractFieldMDArray()
    5512          62 :     {
    5513          62 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
    5514         124 :     }
    5515             : 
    5516          40 :     bool IsWritable() const override
    5517             :     {
    5518          40 :         return m_poParent->IsWritable();
    5519             :     }
    5520             : 
    5521         204 :     const std::string &GetFilename() const override
    5522             :     {
    5523         204 :         return m_poParent->GetFilename();
    5524             :     }
    5525             : 
    5526             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5527         300 :     GetDimensions() const override
    5528             :     {
    5529         300 :         return m_poParent->GetDimensions();
    5530             :     }
    5531             : 
    5532         245 :     const GDALExtendedDataType &GetDataType() const override
    5533             :     {
    5534         245 :         return m_dt;
    5535             :     }
    5536             : 
    5537           2 :     const std::string &GetUnit() const override
    5538             :     {
    5539           2 :         return m_poParent->GetUnit();
    5540             :     }
    5541             : 
    5542           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5543             :     {
    5544           2 :         return m_poParent->GetSpatialRef();
    5545             :     }
    5546             : 
    5547          56 :     const void *GetRawNoDataValue() const override
    5548             :     {
    5549          56 :         const void *parentNoData = m_poParent->GetRawNoDataValue();
    5550          56 :         if (parentNoData == nullptr)
    5551           1 :             return nullptr;
    5552             : 
    5553          55 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
    5554          55 :         memset(&m_pabyNoData[0], 0, m_dt.GetSize());
    5555             : 
    5556         110 :         std::vector<std::unique_ptr<GDALEDTComponent>> comps;
    5557         110 :         comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
    5558         110 :             new GDALEDTComponent(m_srcCompName, 0, m_dt)));
    5559          55 :         auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
    5560         165 :                                                 std::move(comps)));
    5561             : 
    5562          55 :         GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
    5563          55 :                                         &m_pabyNoData[0], tmpDT);
    5564             : 
    5565          55 :         return &m_pabyNoData[0];
    5566             :     }
    5567             : 
    5568           2 :     double GetOffset(bool *pbHasOffset,
    5569             :                      GDALDataType *peStorageType) const override
    5570             :     {
    5571           2 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5572             :     }
    5573             : 
    5574           2 :     double GetScale(bool *pbHasScale,
    5575             :                     GDALDataType *peStorageType) const override
    5576             :     {
    5577           2 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5578             :     }
    5579             : 
    5580          41 :     std::vector<GUInt64> GetBlockSize() const override
    5581             :     {
    5582          41 :         return m_poParent->GetBlockSize();
    5583             :     }
    5584             : };
    5585             : 
    5586             : /************************************************************************/
    5587             : /*                             IRead()                                  */
    5588             : /************************************************************************/
    5589             : 
    5590          46 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
    5591             :                                     const size_t *count,
    5592             :                                     const GInt64 *arrayStep,
    5593             :                                     const GPtrDiff_t *bufferStride,
    5594             :                                     const GDALExtendedDataType &bufferDataType,
    5595             :                                     void *pDstBuffer) const
    5596             : {
    5597          92 :     std::vector<std::unique_ptr<GDALEDTComponent>> comps;
    5598          92 :     comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
    5599          92 :         new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
    5600             :     auto tmpDT(GDALExtendedDataType::Create(
    5601          92 :         std::string(), bufferDataType.GetSize(), std::move(comps)));
    5602             : 
    5603          46 :     return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
    5604          92 :                             tmpDT, pDstBuffer);
    5605             : }
    5606             : 
    5607             : /************************************************************************/
    5608             : /*                      CreateFieldNameExtractArray()                   */
    5609             : /************************************************************************/
    5610             : 
    5611             : static std::shared_ptr<GDALMDArray>
    5612          63 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
    5613             :                             const std::string &fieldName)
    5614             : {
    5615          63 :     CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
    5616          63 :     const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
    5617         125 :     for (const auto &comp : self->GetDataType().GetComponents())
    5618             :     {
    5619         124 :         if (comp->GetName() == fieldName)
    5620             :         {
    5621          62 :             srcComp = &comp;
    5622          62 :             break;
    5623             :         }
    5624             :     }
    5625          63 :     if (srcComp == nullptr)
    5626             :     {
    5627           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
    5628             :                  fieldName.c_str());
    5629           1 :         return nullptr;
    5630             :     }
    5631          62 :     return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
    5632             : }
    5633             : 
    5634             : /************************************************************************/
    5635             : /*                             GetView()                                */
    5636             : /************************************************************************/
    5637             : 
    5638             : // clang-format off
    5639             : /** Return a view of the array using slicing or field access.
    5640             :  *
    5641             :  * The slice expression uses the same syntax as NumPy basic slicing and
    5642             :  * indexing. See
    5643             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
    5644             :  * Or it can use field access by name. See
    5645             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
    5646             :  *
    5647             :  * Multiple [] bracket elements can be concatenated, with a slice expression
    5648             :  * or field name inside each.
    5649             :  *
    5650             :  * For basic slicing and indexing, inside each [] bracket element, a list of
    5651             :  * indexes that apply to successive source dimensions, can be specified, using
    5652             :  * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
    5653             :  * or newaxis, using a comma separator.
    5654             :  *
    5655             :  * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
    5656             :  * <ul>
    5657             :  * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
    5658             :  *     at index 1 in the first dimension, and index 2 in the second dimension
    5659             :  *     from the source array. That is 5</li>
    5660             :  * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
    5661             :  * implemented internally doing this intermediate slicing approach.</li>
    5662             :  * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
    5663             :  * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
    5664             :  *     first dimension. That is [4,5,6,7].</li>
    5665             :  * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
    5666             :  *     second dimension. That is [2,6].</li>
    5667             :  * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
    5668             :  * the second dimension. That is [[2],[6]].</li>
    5669             :  * <li>GetView("[::,2]"): Same as
    5670             :  * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
    5671             :  * ellipsis only expands to one dimension here.</li>
    5672             :  * <li>GetView("[:,::2]"):
    5673             :  * returns a 2-dimensional array, with even-indexed elements of the second
    5674             :  * dimension. That is [[0,2],[4,6]].</li>
    5675             :  * <li>GetView("[:,1::2]"): returns a
    5676             :  * 2-dimensional array, with odd-indexed elements of the second dimension. That
    5677             :  * is [[1,3],[5,7]].</li>
    5678             :  * <li>GetView("[:,1:3:]"): returns a 2-dimensional
    5679             :  * array, with elements of the second dimension with index in the range [1,3[.
    5680             :  * That is [[1,2],[5,6]].</li>
    5681             :  * <li>GetView("[::-1,:]"): returns a 2-dimensional
    5682             :  * array, with the values in first dimension reversed. That is
    5683             :  * [[4,5,6,7],[0,1,2,3]].</li>
    5684             :  * <li>GetView("[newaxis,...]"): returns a
    5685             :  * 3-dimensional array, with an additional dimension of size 1 put at the
    5686             :  * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
    5687             :  * </ul>
    5688             :  *
    5689             :  * One difference with NumPy behavior is that ranges that would result in
    5690             :  * zero elements are not allowed (dimensions of size 0 not being allowed in the
    5691             :  * GDAL multidimensional model).
    5692             :  *
    5693             :  * For field access, the syntax to use is ["field_name"] or ['field_name'].
    5694             :  * Multiple field specification is not supported currently.
    5695             :  *
    5696             :  * Both type of access can be combined, e.g. GetView("[1]['field_name']")
    5697             :  *
    5698             :  * \note When using the GDAL Python bindings, natural Python syntax can be
    5699             :  * used. That is ar[0,::,1]["foo"] will be internally translated to
    5700             :  * ar.GetView("[0,::,1]['foo']")
    5701             :  * \note When using the C++ API and integer indexing only, you may use the
    5702             :  * at(idx0, idx1, ...) method.
    5703             :  *
    5704             :  * The returned array holds a reference to the original one, and thus is
    5705             :  * a view of it (not a copy). If the content of the original array changes,
    5706             :  * the content of the view array too. When using basic slicing and indexing,
    5707             :  * the view can be written if the underlying array is writable.
    5708             :  *
    5709             :  * This is the same as the C function GDALMDArrayGetView()
    5710             :  *
    5711             :  * @param viewExpr Expression expressing basic slicing and indexing, or field
    5712             :  * access.
    5713             :  * @return a new array, that holds a reference to the original one, and thus is
    5714             :  * a view of it (not a copy), or nullptr in case of error.
    5715             :  */
    5716             : // clang-format on
    5717             : 
    5718             : std::shared_ptr<GDALMDArray>
    5719         598 : GDALMDArray::GetView(const std::string &viewExpr) const
    5720             : {
    5721        1196 :     std::vector<ViewSpec> viewSpecs;
    5722        1196 :     return GetView(viewExpr, true, viewSpecs);
    5723             : }
    5724             : 
    5725             : //! @cond Doxygen_Suppress
    5726             : std::shared_ptr<GDALMDArray>
    5727         661 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
    5728             :                      std::vector<ViewSpec> &viewSpecs) const
    5729             : {
    5730        1322 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    5731         661 :     if (!self)
    5732             :     {
    5733           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    5734             :                  "Driver implementation issue: m_pSelf not set !");
    5735           1 :         return nullptr;
    5736             :     }
    5737         660 :     std::string curExpr(viewExpr);
    5738             :     while (true)
    5739             :     {
    5740         663 :         if (curExpr.empty() || curExpr[0] != '[')
    5741             :         {
    5742           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    5743             :                      "Slice string should start with ['");
    5744         660 :             return nullptr;
    5745             :         }
    5746             : 
    5747         661 :         std::string fieldName;
    5748             :         size_t endExpr;
    5749         661 :         if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
    5750             :         {
    5751          67 :             if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
    5752             :             {
    5753           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5754             :                          "Field access not allowed on non-compound data type");
    5755           2 :                 return nullptr;
    5756             :             }
    5757          65 :             size_t idx = 2;
    5758         572 :             for (; idx < curExpr.size(); idx++)
    5759             :             {
    5760         571 :                 const char ch = curExpr[idx];
    5761         571 :                 if (ch == curExpr[1])
    5762          64 :                     break;
    5763         507 :                 if (ch == '\\' && idx + 1 < curExpr.size())
    5764             :                 {
    5765           1 :                     fieldName += curExpr[idx + 1];
    5766           1 :                     idx++;
    5767             :                 }
    5768             :                 else
    5769             :                 {
    5770         506 :                     fieldName += ch;
    5771             :                 }
    5772             :             }
    5773          65 :             if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
    5774             :             {
    5775           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5776             :                          "Invalid field access specification");
    5777           2 :                 return nullptr;
    5778             :             }
    5779          63 :             endExpr = idx + 1;
    5780             :         }
    5781             :         else
    5782             :         {
    5783         594 :             endExpr = curExpr.find(']');
    5784             :         }
    5785         657 :         if (endExpr == std::string::npos)
    5786             :         {
    5787           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
    5788           1 :             return nullptr;
    5789             :         }
    5790         656 :         if (endExpr == 1)
    5791             :         {
    5792           1 :             CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
    5793           1 :             return nullptr;
    5794             :         }
    5795         655 :         std::string activeSlice(curExpr.substr(1, endExpr - 1));
    5796             : 
    5797         655 :         if (!fieldName.empty())
    5798             :         {
    5799         126 :             ViewSpec viewSpec;
    5800          63 :             viewSpec.m_osFieldName = fieldName;
    5801          63 :             viewSpecs.emplace_back(std::move(viewSpec));
    5802             :         }
    5803             : 
    5804         655 :         auto newArray = !fieldName.empty()
    5805             :                             ? CreateFieldNameExtractArray(self, fieldName)
    5806             :                             : CreateSlicedArray(self, viewExpr, activeSlice,
    5807         655 :                                                 bRenameDimensions, viewSpecs);
    5808             : 
    5809         655 :         if (endExpr == curExpr.size() - 1)
    5810             :         {
    5811         652 :             return newArray;
    5812             :         }
    5813           3 :         self = std::move(newArray);
    5814           3 :         curExpr = curExpr.substr(endExpr + 1);
    5815           3 :     }
    5816             : }
    5817             : 
    5818             : //! @endcond
    5819             : 
    5820             : std::shared_ptr<GDALMDArray>
    5821          19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
    5822             : {
    5823          19 :     std::string osExpr("[");
    5824          19 :     bool bFirst = true;
    5825          45 :     for (const auto &idx : indices)
    5826             :     {
    5827          26 :         if (!bFirst)
    5828           7 :             osExpr += ',';
    5829          26 :         bFirst = false;
    5830          26 :         osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
    5831             :     }
    5832          57 :     return GetView(osExpr + ']');
    5833             : }
    5834             : 
    5835             : /************************************************************************/
    5836             : /*                            operator[]                                */
    5837             : /************************************************************************/
    5838             : 
    5839             : /** Return a view of the array using field access
    5840             :  *
    5841             :  * Equivalent of GetView("['fieldName']")
    5842             :  *
    5843             :  * \note When operationg on a shared_ptr, use (*array)["fieldName"] syntax.
    5844             :  */
    5845             : std::shared_ptr<GDALMDArray>
    5846           2 : GDALMDArray::operator[](const std::string &fieldName) const
    5847             : {
    5848           2 :     return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
    5849           4 :                                             .replaceAll('\\', "\\\\")
    5850           4 :                                             .replaceAll('\'', "\\\'")
    5851           6 :                                             .c_str()));
    5852             : }
    5853             : 
    5854             : /************************************************************************/
    5855             : /*                      GDALMDArrayTransposed                           */
    5856             : /************************************************************************/
    5857             : 
    5858             : class GDALMDArrayTransposed final : public GDALPamMDArray
    5859             : {
    5860             :   private:
    5861             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5862             :     std::vector<int> m_anMapNewAxisToOldAxis{};
    5863             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    5864             : 
    5865             :     mutable std::vector<GUInt64> m_parentStart;
    5866             :     mutable std::vector<size_t> m_parentCount;
    5867             :     mutable std::vector<GInt64> m_parentStep;
    5868             :     mutable std::vector<GPtrDiff_t> m_parentStride;
    5869             : 
    5870             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
    5871             :                              const GInt64 *arrayStep,
    5872             :                              const GPtrDiff_t *bufferStride) const;
    5873             : 
    5874             :     static std::string
    5875          84 :     MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
    5876             :     {
    5877          84 :         std::string ret;
    5878          84 :         ret += '[';
    5879         312 :         for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
    5880             :         {
    5881         228 :             if (i > 0)
    5882         144 :                 ret += ',';
    5883         228 :             ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
    5884             :         }
    5885          84 :         ret += ']';
    5886          84 :         return ret;
    5887             :     }
    5888             : 
    5889             :   protected:
    5890          42 :     GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
    5891             :                           const std::vector<int> &anMapNewAxisToOldAxis,
    5892             :                           std::vector<std::shared_ptr<GDALDimension>> &&dims)
    5893          84 :         : GDALAbstractMDArray(std::string(),
    5894          84 :                               "Transposed view of " + poParent->GetFullName() +
    5895          84 :                                   " along " +
    5896          42 :                                   MappingToStr(anMapNewAxisToOldAxis)),
    5897          84 :           GDALPamMDArray(std::string(),
    5898          84 :                          "Transposed view of " + poParent->GetFullName() +
    5899         168 :                              " along " + MappingToStr(anMapNewAxisToOldAxis),
    5900          84 :                          GDALPamMultiDim::GetPAM(poParent),
    5901             :                          poParent->GetContext()),
    5902          42 :           m_poParent(std::move(poParent)),
    5903             :           m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
    5904          42 :           m_dims(std::move(dims)),
    5905          42 :           m_parentStart(m_poParent->GetDimensionCount()),
    5906          42 :           m_parentCount(m_poParent->GetDimensionCount()),
    5907          42 :           m_parentStep(m_poParent->GetDimensionCount()),
    5908         336 :           m_parentStride(m_poParent->GetDimensionCount())
    5909             :     {
    5910          42 :     }
    5911             : 
    5912             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5913             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5914             :                const GDALExtendedDataType &bufferDataType,
    5915             :                void *pDstBuffer) const override;
    5916             : 
    5917             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    5918             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5919             :                 const GDALExtendedDataType &bufferDataType,
    5920             :                 const void *pSrcBuffer) override;
    5921             : 
    5922             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5923             :                      CSLConstList papszOptions) const override;
    5924             : 
    5925             :   public:
    5926             :     static std::shared_ptr<GDALMDArrayTransposed>
    5927          42 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5928             :            const std::vector<int> &anMapNewAxisToOldAxis)
    5929             :     {
    5930          42 :         const auto &parentDims(poParent->GetDimensions());
    5931          84 :         std::vector<std::shared_ptr<GDALDimension>> dims;
    5932         156 :         for (const auto iOldAxis : anMapNewAxisToOldAxis)
    5933             :         {
    5934         114 :             if (iOldAxis < 0)
    5935             :             {
    5936           1 :                 dims.push_back(std::make_shared<GDALDimension>(
    5937           2 :                     std::string(), "newaxis", std::string(), std::string(), 1));
    5938             :             }
    5939             :             else
    5940             :             {
    5941         113 :                 dims.emplace_back(parentDims[iOldAxis]);
    5942             :             }
    5943             :         }
    5944             : 
    5945             :         auto newAr(
    5946             :             std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
    5947          42 :                 poParent, anMapNewAxisToOldAxis, std::move(dims))));
    5948          42 :         newAr->SetSelf(newAr);
    5949          84 :         return newAr;
    5950             :     }
    5951             : 
    5952           1 :     bool IsWritable() const override
    5953             :     {
    5954           1 :         return m_poParent->IsWritable();
    5955             :     }
    5956             : 
    5957          84 :     const std::string &GetFilename() const override
    5958             :     {
    5959          84 :         return m_poParent->GetFilename();
    5960             :     }
    5961             : 
    5962             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5963         358 :     GetDimensions() const override
    5964             :     {
    5965         358 :         return m_dims;
    5966             :     }
    5967             : 
    5968         141 :     const GDALExtendedDataType &GetDataType() const override
    5969             :     {
    5970         141 :         return m_poParent->GetDataType();
    5971             :     }
    5972             : 
    5973           4 :     const std::string &GetUnit() const override
    5974             :     {
    5975           4 :         return m_poParent->GetUnit();
    5976             :     }
    5977             : 
    5978           5 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5979             :     {
    5980          10 :         auto poSrcSRS = m_poParent->GetSpatialRef();
    5981           5 :         if (!poSrcSRS)
    5982           2 :             return nullptr;
    5983           6 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
    5984           6 :         std::vector<int> dstMapping;
    5985           9 :         for (int srcAxis : srcMapping)
    5986             :         {
    5987           6 :             bool bFound = false;
    5988          14 :             for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
    5989             :             {
    5990          14 :                 if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
    5991             :                 {
    5992           6 :                     dstMapping.push_back(static_cast<int>(i) + 1);
    5993           6 :                     bFound = true;
    5994           6 :                     break;
    5995             :                 }
    5996             :             }
    5997           6 :             if (!bFound)
    5998             :             {
    5999           0 :                 dstMapping.push_back(0);
    6000             :             }
    6001             :         }
    6002           6 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
    6003           3 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
    6004           3 :         return poClone;
    6005             :     }
    6006             : 
    6007           4 :     const void *GetRawNoDataValue() const override
    6008             :     {
    6009           4 :         return m_poParent->GetRawNoDataValue();
    6010             :     }
    6011             : 
    6012             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
    6013             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
    6014             : 
    6015           4 :     double GetOffset(bool *pbHasOffset,
    6016             :                      GDALDataType *peStorageType) const override
    6017             :     {
    6018           4 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    6019             :     }
    6020             : 
    6021           4 :     double GetScale(bool *pbHasScale,
    6022             :                     GDALDataType *peStorageType) const override
    6023             :     {
    6024           4 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    6025             :     }
    6026             : 
    6027             :     // bool SetOffset(double dfOffset) override { return
    6028             :     // m_poParent->SetOffset(dfOffset); }
    6029             : 
    6030             :     // bool SetScale(double dfScale) override { return
    6031             :     // m_poParent->SetScale(dfScale); }
    6032             : 
    6033           3 :     std::vector<GUInt64> GetBlockSize() const override
    6034             :     {
    6035           3 :         std::vector<GUInt64> ret(GetDimensionCount());
    6036           6 :         const auto parentBlockSize(m_poParent->GetBlockSize());
    6037          11 :         for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
    6038             :         {
    6039           8 :             const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
    6040           8 :             if (iOldAxis >= 0)
    6041             :             {
    6042           7 :                 ret[i] = parentBlockSize[iOldAxis];
    6043             :             }
    6044             :         }
    6045           6 :         return ret;
    6046             :     }
    6047             : 
    6048             :     std::shared_ptr<GDALAttribute>
    6049           1 :     GetAttribute(const std::string &osName) const override
    6050             :     {
    6051           1 :         return m_poParent->GetAttribute(osName);
    6052             :     }
    6053             : 
    6054             :     std::vector<std::shared_ptr<GDALAttribute>>
    6055           6 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    6056             :     {
    6057           6 :         return m_poParent->GetAttributes(papszOptions);
    6058             :     }
    6059             : };
    6060             : 
    6061             : /************************************************************************/
    6062             : /*                         PrepareParentArrays()                        */
    6063             : /************************************************************************/
    6064             : 
    6065          47 : void GDALMDArrayTransposed::PrepareParentArrays(
    6066             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    6067             :     const GPtrDiff_t *bufferStride) const
    6068             : {
    6069         176 :     for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
    6070             :     {
    6071         129 :         const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
    6072         129 :         if (iOldAxis >= 0)
    6073             :         {
    6074         128 :             m_parentStart[iOldAxis] = arrayStartIdx[i];
    6075         128 :             m_parentCount[iOldAxis] = count[i];
    6076         128 :             if (arrayStep)  // only null when called from IAdviseRead()
    6077             :             {
    6078         126 :                 m_parentStep[iOldAxis] = arrayStep[i];
    6079             :             }
    6080         128 :             if (bufferStride)  // only null when called from IAdviseRead()
    6081             :             {
    6082         126 :                 m_parentStride[iOldAxis] = bufferStride[i];
    6083             :             }
    6084             :         }
    6085             :     }
    6086          47 : }
    6087             : 
    6088             : /************************************************************************/
    6089             : /*                             IRead()                                  */
    6090             : /************************************************************************/
    6091             : 
    6092          44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
    6093             :                                   const size_t *count, const GInt64 *arrayStep,
    6094             :                                   const GPtrDiff_t *bufferStride,
    6095             :                                   const GDALExtendedDataType &bufferDataType,
    6096             :                                   void *pDstBuffer) const
    6097             : {
    6098          44 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    6099          88 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
    6100          44 :                             m_parentStep.data(), m_parentStride.data(),
    6101          44 :                             bufferDataType, pDstBuffer);
    6102             : }
    6103             : 
    6104             : /************************************************************************/
    6105             : /*                            IWrite()                                  */
    6106             : /************************************************************************/
    6107             : 
    6108           2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
    6109             :                                    const size_t *count, const GInt64 *arrayStep,
    6110             :                                    const GPtrDiff_t *bufferStride,
    6111             :                                    const GDALExtendedDataType &bufferDataType,
    6112             :                                    const void *pSrcBuffer)
    6113             : {
    6114           2 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    6115           4 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
    6116           2 :                              m_parentStep.data(), m_parentStride.data(),
    6117           2 :                              bufferDataType, pSrcBuffer);
    6118             : }
    6119             : 
    6120             : /************************************************************************/
    6121             : /*                             IAdviseRead()                            */
    6122             : /************************************************************************/
    6123             : 
    6124           1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
    6125             :                                         const size_t *count,
    6126             :                                         CSLConstList papszOptions) const
    6127             : {
    6128           1 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
    6129           1 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
    6130           1 :                                   papszOptions);
    6131             : }
    6132             : 
    6133             : /************************************************************************/
    6134             : /*                           Transpose()                                */
    6135             : /************************************************************************/
    6136             : 
    6137             : /** Return a view of the array whose axis have been reordered.
    6138             :  *
    6139             :  * The anMapNewAxisToOldAxis parameter should contain all the values between 0
    6140             :  * and GetDimensionCount() - 1, and each only once.
    6141             :  * -1 can be used as a special index value to ask for the insertion of a new
    6142             :  * axis of size 1.
    6143             :  * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
    6144             :  * index of one of its dimension, it corresponds to the axis of index
    6145             :  * anMapNewAxisToOldAxis[i] from the current array.
    6146             :  *
    6147             :  * This is similar to the numpy.transpose() method
    6148             :  *
    6149             :  * The returned array holds a reference to the original one, and thus is
    6150             :  * a view of it (not a copy). If the content of the original array changes,
    6151             :  * the content of the view array too. The view can be written if the underlying
    6152             :  * array is writable.
    6153             :  *
    6154             :  * Note that I/O performance in such a transposed view might be poor.
    6155             :  *
    6156             :  * This is the same as the C function GDALMDArrayTranspose().
    6157             :  *
    6158             :  * @return a new array, that holds a reference to the original one, and thus is
    6159             :  * a view of it (not a copy), or nullptr in case of error.
    6160             :  */
    6161             : std::shared_ptr<GDALMDArray>
    6162          50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
    6163             : {
    6164         100 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    6165          50 :     if (!self)
    6166             :     {
    6167           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6168             :                  "Driver implementation issue: m_pSelf not set !");
    6169           0 :         return nullptr;
    6170             :     }
    6171          50 :     const int nDims = static_cast<int>(GetDimensionCount());
    6172         100 :     std::vector<bool> alreadyUsedOldAxis(nDims, false);
    6173          50 :     int nCountOldAxis = 0;
    6174         179 :     for (const auto iOldAxis : anMapNewAxisToOldAxis)
    6175             :     {
    6176         133 :         if (iOldAxis < -1 || iOldAxis >= nDims)
    6177             :         {
    6178           3 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
    6179           4 :             return nullptr;
    6180             :         }
    6181         130 :         if (iOldAxis >= 0)
    6182             :         {
    6183         128 :             if (alreadyUsedOldAxis[iOldAxis])
    6184             :             {
    6185           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
    6186             :                          iOldAxis);
    6187           1 :                 return nullptr;
    6188             :             }
    6189         127 :             alreadyUsedOldAxis[iOldAxis] = true;
    6190         127 :             nCountOldAxis++;
    6191             :         }
    6192             :     }
    6193          46 :     if (nCountOldAxis != nDims)
    6194             :     {
    6195           4 :         CPLError(CE_Failure, CPLE_AppDefined,
    6196             :                  "One or several original axis missing");
    6197           4 :         return nullptr;
    6198             :     }
    6199          42 :     return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
    6200             : }
    6201             : 
    6202             : /************************************************************************/
    6203             : /*                             IRead()                                  */
    6204             : /************************************************************************/
    6205             : 
    6206          16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
    6207             :                                 const size_t *count, const GInt64 *arrayStep,
    6208             :                                 const GPtrDiff_t *bufferStride,
    6209             :                                 const GDALExtendedDataType &bufferDataType,
    6210             :                                 void *pDstBuffer) const
    6211             : {
    6212          16 :     const double dfScale = m_dfScale;
    6213          16 :     const double dfOffset = m_dfOffset;
    6214          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
    6215             :     const auto dtDouble =
    6216          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
    6217          16 :     const size_t nDTSize = dtDouble.GetSize();
    6218          16 :     const bool bTempBufferNeeded = (dtDouble != bufferDataType);
    6219             : 
    6220          16 :     double adfSrcNoData[2] = {0, 0};
    6221          16 :     if (m_bHasNoData)
    6222             :     {
    6223           9 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
    6224           9 :                                         m_poParent->GetDataType(),
    6225             :                                         &adfSrcNoData[0], dtDouble);
    6226             :     }
    6227             : 
    6228          16 :     const auto nDims = GetDimensions().size();
    6229          16 :     if (nDims == 0)
    6230             :     {
    6231             :         double adfVal[2];
    6232           9 :         if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
    6233             :                               dtDouble, &adfVal[0]))
    6234             :         {
    6235           0 :             return false;
    6236             :         }
    6237           9 :         if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
    6238             :         {
    6239           6 :             adfVal[0] = adfVal[0] * dfScale + dfOffset;
    6240           6 :             if (bDTIsComplex)
    6241             :             {
    6242           2 :                 adfVal[1] = adfVal[1] * dfScale + dfOffset;
    6243             :             }
    6244           6 :             GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
    6245             :                                             bufferDataType);
    6246             :         }
    6247             :         else
    6248             :         {
    6249           3 :             GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
    6250             :                                             pDstBuffer, bufferDataType);
    6251             :         }
    6252           9 :         return true;
    6253             :     }
    6254             : 
    6255          14 :     std::vector<GPtrDiff_t> actualBufferStrideVector;
    6256           7 :     const GPtrDiff_t *actualBufferStridePtr = bufferStride;
    6257           7 :     void *pTempBuffer = pDstBuffer;
    6258           7 :     if (bTempBufferNeeded)
    6259             :     {
    6260           2 :         size_t nElts = 1;
    6261           2 :         actualBufferStrideVector.resize(nDims);
    6262           7 :         for (size_t i = 0; i < nDims; i++)
    6263           5 :             nElts *= count[i];
    6264           2 :         actualBufferStrideVector.back() = 1;
    6265           5 :         for (size_t i = nDims - 1; i > 0;)
    6266             :         {
    6267           3 :             --i;
    6268           3 :             actualBufferStrideVector[i] =
    6269           3 :                 actualBufferStrideVector[i + 1] * count[i + 1];
    6270             :         }
    6271           2 :         actualBufferStridePtr = actualBufferStrideVector.data();
    6272           2 :         pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
    6273           2 :         if (!pTempBuffer)
    6274           0 :             return false;
    6275             :     }
    6276           7 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
    6277             :                           actualBufferStridePtr, dtDouble, pTempBuffer))
    6278             :     {
    6279           0 :         if (bTempBufferNeeded)
    6280           0 :             VSIFree(pTempBuffer);
    6281           0 :         return false;
    6282             :     }
    6283             : 
    6284             :     struct Stack
    6285             :     {
    6286             :         size_t nIters = 0;
    6287             :         double *src_ptr = nullptr;
    6288             :         GByte *dst_ptr = nullptr;
    6289             :         GPtrDiff_t src_inc_offset = 0;
    6290             :         GPtrDiff_t dst_inc_offset = 0;
    6291             :     };
    6292             : 
    6293           7 :     std::vector<Stack> stack(nDims);
    6294           7 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    6295          23 :     for (size_t i = 0; i < nDims; i++)
    6296             :     {
    6297          32 :         stack[i].src_inc_offset =
    6298          16 :             actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
    6299          16 :         stack[i].dst_inc_offset =
    6300          16 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6301             :     }
    6302           7 :     stack[0].src_ptr = static_cast<double *>(pTempBuffer);
    6303           7 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    6304             : 
    6305           7 :     size_t dimIdx = 0;
    6306           7 :     const size_t nDimsMinus1 = nDims - 1;
    6307             :     GByte abyDstNoData[16];
    6308           7 :     CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
    6309           7 :     GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
    6310             :                                     bufferDataType);
    6311             : 
    6312          37 : lbl_next_depth:
    6313          37 :     if (dimIdx == nDimsMinus1)
    6314             :     {
    6315          25 :         auto nIters = count[dimIdx];
    6316          25 :         double *padfVal = stack[dimIdx].src_ptr;
    6317          25 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
    6318             :         while (true)
    6319             :         {
    6320          92 :             if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
    6321             :             {
    6322          88 :                 padfVal[0] = padfVal[0] * dfScale + dfOffset;
    6323          88 :                 if (bDTIsComplex)
    6324             :                 {
    6325           1 :                     padfVal[1] = padfVal[1] * dfScale + dfOffset;
    6326             :                 }
    6327          88 :                 if (bTempBufferNeeded)
    6328             :                 {
    6329          29 :                     GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
    6330             :                                                     dst_ptr, bufferDataType);
    6331             :                 }
    6332             :             }
    6333             :             else
    6334             :             {
    6335           4 :                 memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
    6336             :             }
    6337             : 
    6338          92 :             if ((--nIters) == 0)
    6339          25 :                 break;
    6340          67 :             padfVal += stack[dimIdx].src_inc_offset;
    6341          67 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    6342             :         }
    6343             :     }
    6344             :     else
    6345             :     {
    6346          12 :         stack[dimIdx].nIters = count[dimIdx];
    6347             :         while (true)
    6348             :         {
    6349          30 :             dimIdx++;
    6350          30 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    6351          30 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6352          30 :             goto lbl_next_depth;
    6353          30 :         lbl_return_to_caller:
    6354          30 :             dimIdx--;
    6355          30 :             if ((--stack[dimIdx].nIters) == 0)
    6356          12 :                 break;
    6357          18 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    6358          18 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6359             :         }
    6360             :     }
    6361          37 :     if (dimIdx > 0)
    6362          30 :         goto lbl_return_to_caller;
    6363             : 
    6364           7 :     if (bTempBufferNeeded)
    6365           2 :         VSIFree(pTempBuffer);
    6366           7 :     return true;
    6367             : }
    6368             : 
    6369             : /************************************************************************/
    6370             : /*                             IWrite()                                 */
    6371             : /************************************************************************/
    6372             : 
    6373          16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
    6374             :                                  const size_t *count, const GInt64 *arrayStep,
    6375             :                                  const GPtrDiff_t *bufferStride,
    6376             :                                  const GDALExtendedDataType &bufferDataType,
    6377             :                                  const void *pSrcBuffer)
    6378             : {
    6379          16 :     const double dfScale = m_dfScale;
    6380          16 :     const double dfOffset = m_dfOffset;
    6381          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
    6382             :     const auto dtDouble =
    6383          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
    6384          16 :     const size_t nDTSize = dtDouble.GetSize();
    6385          16 :     const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
    6386             :     const bool bSelfAndParentHaveNoData =
    6387          16 :         m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
    6388          16 :     double dfNoData = 0;
    6389          16 :     if (m_bHasNoData)
    6390             :     {
    6391           7 :         GDALCopyWords(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
    6392             :                       &dfNoData, GDT_Float64, 0, 1);
    6393             :     }
    6394             : 
    6395          16 :     double adfSrcNoData[2] = {0, 0};
    6396          16 :     if (bSelfAndParentHaveNoData)
    6397             :     {
    6398           7 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
    6399           7 :                                         m_poParent->GetDataType(),
    6400             :                                         &adfSrcNoData[0], dtDouble);
    6401             :     }
    6402             : 
    6403          16 :     const auto nDims = GetDimensions().size();
    6404          16 :     if (nDims == 0)
    6405             :     {
    6406             :         double adfVal[2];
    6407          10 :         GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
    6408             :                                         dtDouble);
    6409          16 :         if (bSelfAndParentHaveNoData &&
    6410           6 :             (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
    6411             :         {
    6412           4 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
    6413           2 :                                      bufferStride, m_poParent->GetDataType(),
    6414           4 :                                      m_poParent->GetRawNoDataValue());
    6415             :         }
    6416             :         else
    6417             :         {
    6418           8 :             adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
    6419           8 :             if (bDTIsComplex)
    6420             :             {
    6421           2 :                 adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
    6422             :             }
    6423           8 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
    6424           8 :                                      bufferStride, dtDouble, &adfVal[0]);
    6425             :         }
    6426             :     }
    6427             : 
    6428          12 :     std::vector<GPtrDiff_t> tmpBufferStrideVector;
    6429           6 :     size_t nElts = 1;
    6430           6 :     tmpBufferStrideVector.resize(nDims);
    6431          20 :     for (size_t i = 0; i < nDims; i++)
    6432          14 :         nElts *= count[i];
    6433           6 :     tmpBufferStrideVector.back() = 1;
    6434          14 :     for (size_t i = nDims - 1; i > 0;)
    6435             :     {
    6436           8 :         --i;
    6437           8 :         tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
    6438             :     }
    6439           6 :     const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
    6440           6 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
    6441           6 :     if (!pTempBuffer)
    6442           0 :         return false;
    6443             : 
    6444             :     struct Stack
    6445             :     {
    6446             :         size_t nIters = 0;
    6447             :         double *dst_ptr = nullptr;
    6448             :         const GByte *src_ptr = nullptr;
    6449             :         GPtrDiff_t src_inc_offset = 0;
    6450             :         GPtrDiff_t dst_inc_offset = 0;
    6451             :     };
    6452             : 
    6453           6 :     std::vector<Stack> stack(nDims);
    6454           6 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    6455          20 :     for (size_t i = 0; i < nDims; i++)
    6456             :     {
    6457          28 :         stack[i].dst_inc_offset =
    6458          14 :             tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
    6459          14 :         stack[i].src_inc_offset =
    6460          14 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6461             :     }
    6462           6 :     stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
    6463           6 :     stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
    6464             : 
    6465           6 :     size_t dimIdx = 0;
    6466           6 :     const size_t nDimsMinus1 = nDims - 1;
    6467             : 
    6468          34 : lbl_next_depth:
    6469          34 :     if (dimIdx == nDimsMinus1)
    6470             :     {
    6471          23 :         auto nIters = count[dimIdx];
    6472          23 :         double *dst_ptr = stack[dimIdx].dst_ptr;
    6473          23 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
    6474             :         while (true)
    6475             :         {
    6476             :             double adfVal[2];
    6477             :             const double *padfSrcVal;
    6478          86 :             if (bIsBufferDataTypeNativeDataType)
    6479             :             {
    6480          50 :                 padfSrcVal = reinterpret_cast<const double *>(src_ptr);
    6481             :             }
    6482             :             else
    6483             :             {
    6484          36 :                 GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
    6485             :                                                 &adfVal[0], dtDouble);
    6486          36 :                 padfSrcVal = adfVal;
    6487             :             }
    6488             : 
    6489         148 :             if (bSelfAndParentHaveNoData &&
    6490          62 :                 (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
    6491             :             {
    6492           3 :                 dst_ptr[0] = adfSrcNoData[0];
    6493           3 :                 if (bDTIsComplex)
    6494             :                 {
    6495           1 :                     dst_ptr[1] = adfSrcNoData[1];
    6496             :                 }
    6497             :             }
    6498             :             else
    6499             :             {
    6500          83 :                 dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
    6501          83 :                 if (bDTIsComplex)
    6502             :                 {
    6503           1 :                     dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
    6504             :                 }
    6505             :             }
    6506             : 
    6507          86 :             if ((--nIters) == 0)
    6508          23 :                 break;
    6509          63 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    6510          63 :             src_ptr += stack[dimIdx].src_inc_offset;
    6511          63 :         }
    6512             :     }
    6513             :     else
    6514             :     {
    6515          11 :         stack[dimIdx].nIters = count[dimIdx];
    6516             :         while (true)
    6517             :         {
    6518          28 :             dimIdx++;
    6519          28 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    6520          28 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6521          28 :             goto lbl_next_depth;
    6522          28 :         lbl_return_to_caller:
    6523          28 :             dimIdx--;
    6524          28 :             if ((--stack[dimIdx].nIters) == 0)
    6525          11 :                 break;
    6526          17 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    6527          17 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6528             :         }
    6529             :     }
    6530          34 :     if (dimIdx > 0)
    6531          28 :         goto lbl_return_to_caller;
    6532             : 
    6533             :     // If the parent array is not double/complex-double, then convert the
    6534             :     // values to it, before calling Write(), as some implementations can be
    6535             :     // very slow when doing the type conversion.
    6536           6 :     const auto &eParentDT = m_poParent->GetDataType();
    6537           6 :     const size_t nParentDTSize = eParentDT.GetSize();
    6538           6 :     if (nParentDTSize <= nDTSize / 2)
    6539             :     {
    6540             :         // Copy in-place by making sure that source and target do not overlap
    6541           6 :         const auto eNumericDT = dtDouble.GetNumericDataType();
    6542           6 :         const auto eParentNumericDT = eParentDT.GetNumericDataType();
    6543             : 
    6544             :         // Copy first element
    6545             :         {
    6546           6 :             std::vector<GByte> abyTemp(nParentDTSize);
    6547           6 :             GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
    6548           6 :                             static_cast<int>(nDTSize), &abyTemp[0],
    6549             :                             eParentNumericDT, static_cast<int>(nParentDTSize),
    6550             :                             1);
    6551           6 :             memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
    6552             :         }
    6553             :         // Remaining elements
    6554          86 :         for (size_t i = 1; i < nElts; ++i)
    6555             :         {
    6556          80 :             GDALCopyWords(static_cast<GByte *>(pTempBuffer) + i * nDTSize,
    6557             :                           eNumericDT, 0,
    6558          80 :                           static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
    6559             :                           eParentNumericDT, 0, 1);
    6560             :         }
    6561             :     }
    6562             : 
    6563             :     const bool ret =
    6564           6 :         m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
    6565             :                           eParentDT, pTempBuffer);
    6566             : 
    6567           6 :     VSIFree(pTempBuffer);
    6568           6 :     return ret;
    6569             : }
    6570             : 
    6571             : /************************************************************************/
    6572             : /*                           GetUnscaled()                              */
    6573             : /************************************************************************/
    6574             : 
    6575             : /** Return an array that is the unscaled version of the current one.
    6576             :  *
    6577             :  * That is each value of the unscaled array will be
    6578             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    6579             :  *
    6580             :  * Starting with GDAL 3.3, the Write() method is implemented and will convert
    6581             :  * from unscaled values to raw values.
    6582             :  *
    6583             :  * This is the same as the C function GDALMDArrayGetUnscaled().
    6584             :  *
    6585             :  * @param dfOverriddenScale Custom scale value instead of GetScale()
    6586             :  * @param dfOverriddenOffset Custom offset value instead of GetOffset()
    6587             :  * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
    6588             :  * @return a new array, that holds a reference to the original one, and thus is
    6589             :  * a view of it (not a copy), or nullptr in case of error.
    6590             :  */
    6591             : std::shared_ptr<GDALMDArray>
    6592          17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
    6593             :                          double dfOverriddenDstNodata) const
    6594             : {
    6595          34 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    6596          17 :     if (!self)
    6597             :     {
    6598           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6599             :                  "Driver implementation issue: m_pSelf not set !");
    6600           0 :         return nullptr;
    6601             :     }
    6602          17 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    6603             :     {
    6604           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6605             :                  "GetUnscaled() only supports numeric data type");
    6606           0 :         return nullptr;
    6607             :     }
    6608             :     const double dfScale =
    6609          17 :         std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
    6610             :     const double dfOffset =
    6611          17 :         std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
    6612          17 :     if (dfScale == 1.0 && dfOffset == 0.0)
    6613           4 :         return self;
    6614             : 
    6615          13 :     GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
    6616          13 :                            ? GDT_CFloat64
    6617          13 :                            : GDT_Float64;
    6618          14 :     if (dfOverriddenScale == -1 && dfOverriddenOffset == 0 &&
    6619           1 :         GetDataType().GetNumericDataType() == GDT_Float32)
    6620           1 :         eDT = GDT_Float32;
    6621             : 
    6622          26 :     return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
    6623          13 :                                        dfOverriddenDstNodata, eDT);
    6624             : }
    6625             : 
    6626             : /************************************************************************/
    6627             : /*                         GDALMDArrayMask                              */
    6628             : /************************************************************************/
    6629             : 
    6630             : class GDALMDArrayMask final : public GDALPamMDArray
    6631             : {
    6632             :   private:
    6633             :     std::shared_ptr<GDALMDArray> m_poParent{};
    6634             :     GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_Byte)};
    6635             :     double m_dfMissingValue = 0.0;
    6636             :     bool m_bHasMissingValue = false;
    6637             :     double m_dfFillValue = 0.0;
    6638             :     bool m_bHasFillValue = false;
    6639             :     double m_dfValidMin = 0.0;
    6640             :     bool m_bHasValidMin = false;
    6641             :     double m_dfValidMax = 0.0;
    6642             :     bool m_bHasValidMax = false;
    6643             :     std::vector<uint32_t> m_anValidFlagMasks{};
    6644             :     std::vector<uint32_t> m_anValidFlagValues{};
    6645             : 
    6646             :     bool Init(CSLConstList papszOptions);
    6647             : 
    6648             :     template <typename Type>
    6649             :     void
    6650             :     ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
    6651             :                  const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    6652             :                  const void *pTempBuffer,
    6653             :                  const GDALExtendedDataType &oTmpBufferDT,
    6654             :                  const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
    6655             : 
    6656             :   protected:
    6657          45 :     explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
    6658          90 :         : GDALAbstractMDArray(std::string(),
    6659          90 :                               "Mask of " + poParent->GetFullName()),
    6660          90 :           GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
    6661          90 :                          GDALPamMultiDim::GetPAM(poParent),
    6662             :                          poParent->GetContext()),
    6663         225 :           m_poParent(std::move(poParent))
    6664             :     {
    6665          45 :     }
    6666             : 
    6667             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6668             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    6669             :                const GDALExtendedDataType &bufferDataType,
    6670             :                void *pDstBuffer) const override;
    6671             : 
    6672           0 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6673             :                      CSLConstList papszOptions) const override
    6674             :     {
    6675           0 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
    6676             :     }
    6677             : 
    6678             :   public:
    6679             :     static std::shared_ptr<GDALMDArrayMask>
    6680             :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    6681             :            CSLConstList papszOptions);
    6682             : 
    6683           1 :     bool IsWritable() const override
    6684             :     {
    6685           1 :         return false;
    6686             :     }
    6687             : 
    6688          48 :     const std::string &GetFilename() const override
    6689             :     {
    6690          48 :         return m_poParent->GetFilename();
    6691             :     }
    6692             : 
    6693             :     const std::vector<std::shared_ptr<GDALDimension>> &
    6694         373 :     GetDimensions() const override
    6695             :     {
    6696         373 :         return m_poParent->GetDimensions();
    6697             :     }
    6698             : 
    6699         132 :     const GDALExtendedDataType &GetDataType() const override
    6700             :     {
    6701         132 :         return m_dt;
    6702             :     }
    6703             : 
    6704           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    6705             :     {
    6706           1 :         return m_poParent->GetSpatialRef();
    6707             :     }
    6708             : 
    6709           2 :     std::vector<GUInt64> GetBlockSize() const override
    6710             :     {
    6711           2 :         return m_poParent->GetBlockSize();
    6712             :     }
    6713             : };
    6714             : 
    6715             : /************************************************************************/
    6716             : /*                    GDALMDArrayMask::Create()                         */
    6717             : /************************************************************************/
    6718             : 
    6719             : /* static */ std::shared_ptr<GDALMDArrayMask>
    6720          45 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
    6721             :                         CSLConstList papszOptions)
    6722             : {
    6723          90 :     auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
    6724          45 :     newAr->SetSelf(newAr);
    6725          45 :     if (!newAr->Init(papszOptions))
    6726           6 :         return nullptr;
    6727          39 :     return newAr;
    6728             : }
    6729             : 
    6730             : /************************************************************************/
    6731             : /*                    GDALMDArrayMask::Init()                           */
    6732             : /************************************************************************/
    6733             : 
    6734          45 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
    6735             : {
    6736             :     const auto GetSingleValNumericAttr =
    6737         180 :         [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
    6738             :     {
    6739         540 :         auto poAttr = m_poParent->GetAttribute(pszAttrName);
    6740         180 :         if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
    6741             :         {
    6742          22 :             const auto anDimSizes = poAttr->GetDimensionsSize();
    6743          21 :             if (anDimSizes.empty() ||
    6744          10 :                 (anDimSizes.size() == 1 && anDimSizes[0] == 1))
    6745             :             {
    6746          11 :                 bHasVal = true;
    6747          11 :                 dfVal = poAttr->ReadAsDouble();
    6748             :             }
    6749             :         }
    6750         180 :     };
    6751             : 
    6752          45 :     GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
    6753          45 :                             m_dfMissingValue);
    6754          45 :     GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
    6755          45 :     GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
    6756          45 :     GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
    6757             : 
    6758             :     {
    6759         135 :         auto poValidRange = m_poParent->GetAttribute("valid_range");
    6760          50 :         if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
    6761          55 :             poValidRange->GetDimensionsSize()[0] == 2 &&
    6762           5 :             poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
    6763             :         {
    6764           5 :             m_bHasValidMin = true;
    6765           5 :             m_bHasValidMax = true;
    6766           5 :             auto vals = poValidRange->ReadAsDoubleArray();
    6767           5 :             CPLAssert(vals.size() == 2);
    6768           5 :             m_dfValidMin = vals[0];
    6769           5 :             m_dfValidMax = vals[1];
    6770             :         }
    6771             :     }
    6772             : 
    6773             :     // Take into account
    6774             :     // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
    6775             :     // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
    6776             :     const char *pszUnmaskFlags =
    6777          45 :         CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
    6778          45 :     if (pszUnmaskFlags)
    6779             :     {
    6780             :         const auto IsScalarStringAttr =
    6781          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
    6782             :         {
    6783          26 :             return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
    6784          26 :                    (poAttr->GetDimensionsSize().empty() ||
    6785          13 :                     (poAttr->GetDimensionsSize().size() == 1 &&
    6786          26 :                      poAttr->GetDimensionsSize()[0] == 1));
    6787             :         };
    6788             : 
    6789          28 :         auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
    6790          14 :         if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
    6791             :         {
    6792           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6793             :                      "UNMASK_FLAGS option specified but array has no "
    6794             :                      "flag_meanings attribute");
    6795           1 :             return false;
    6796             :         }
    6797          13 :         const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
    6798          13 :         if (!pszFlagMeanings)
    6799             :         {
    6800           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6801             :                      "Cannot read flag_meanings attribute");
    6802           1 :             return false;
    6803             :         }
    6804             : 
    6805             :         const auto IsSingleDimNumericAttr =
    6806          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
    6807             :         {
    6808          26 :             return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
    6809          26 :                    poAttr->GetDimensionsSize().size() == 1;
    6810             :         };
    6811             : 
    6812          24 :         auto poFlagValues = m_poParent->GetAttribute("flag_values");
    6813             :         const bool bHasFlagValues =
    6814          12 :             poFlagValues && IsSingleDimNumericAttr(poFlagValues);
    6815             : 
    6816          24 :         auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
    6817             :         const bool bHasFlagMasks =
    6818          12 :             poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
    6819             : 
    6820          12 :         if (!bHasFlagValues && !bHasFlagMasks)
    6821             :         {
    6822           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6823             :                      "Cannot find flag_values and/or flag_masks attribute");
    6824           1 :             return false;
    6825             :         }
    6826             : 
    6827             :         const CPLStringList aosUnmaskFlags(
    6828          11 :             CSLTokenizeString2(pszUnmaskFlags, ",", 0));
    6829             :         const CPLStringList aosFlagMeanings(
    6830          11 :             CSLTokenizeString2(pszFlagMeanings, " ", 0));
    6831             : 
    6832          11 :         if (bHasFlagValues)
    6833             :         {
    6834           7 :             const auto eType = poFlagValues->GetDataType().GetNumericDataType();
    6835             :             // We could support Int64 or UInt64, but more work...
    6836           7 :             if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
    6837           0 :                 eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
    6838             :             {
    6839           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    6840             :                          "Unsupported data type for flag_values attribute: %s",
    6841             :                          GDALGetDataTypeName(eType));
    6842           0 :                 return false;
    6843             :             }
    6844             :         }
    6845             : 
    6846          11 :         if (bHasFlagMasks)
    6847             :         {
    6848           6 :             const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
    6849             :             // We could support Int64 or UInt64, but more work...
    6850           6 :             if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
    6851           0 :                 eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
    6852             :             {
    6853           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    6854             :                          "Unsupported data type for flag_masks attribute: %s",
    6855             :                          GDALGetDataTypeName(eType));
    6856           0 :                 return false;
    6857             :             }
    6858             :         }
    6859             : 
    6860             :         const std::vector<double> adfValues(
    6861             :             bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
    6862          11 :                            : std::vector<double>());
    6863             :         const std::vector<double> adfMasks(
    6864             :             bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
    6865          11 :                           : std::vector<double>());
    6866             : 
    6867          18 :         if (bHasFlagValues &&
    6868           7 :             adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
    6869             :         {
    6870           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6871             :                      "Number of values in flag_values attribute is different "
    6872             :                      "from the one in flag_meanings");
    6873           1 :             return false;
    6874             :         }
    6875             : 
    6876          16 :         if (bHasFlagMasks &&
    6877           6 :             adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
    6878             :         {
    6879           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6880             :                      "Number of values in flag_masks attribute is different "
    6881             :                      "from the one in flag_meanings");
    6882           1 :             return false;
    6883             :         }
    6884             : 
    6885          19 :         for (int i = 0; i < aosUnmaskFlags.size(); ++i)
    6886             :         {
    6887          11 :             const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
    6888          11 :             if (nIdxFlag < 0)
    6889             :             {
    6890           1 :                 CPLError(
    6891             :                     CE_Failure, CPLE_AppDefined,
    6892             :                     "Cannot fing flag %s in flag_meanings = '%s' attribute",
    6893             :                     aosUnmaskFlags[i], pszFlagMeanings);
    6894           1 :                 return false;
    6895             :             }
    6896             : 
    6897          10 :             if (bHasFlagValues && adfValues[nIdxFlag] < 0)
    6898             :             {
    6899           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    6900             :                          "Invalid value in flag_values[%d] = %f", nIdxFlag,
    6901           0 :                          adfValues[nIdxFlag]);
    6902           0 :                 return false;
    6903             :             }
    6904             : 
    6905          10 :             if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
    6906             :             {
    6907           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    6908             :                          "Invalid value in flag_masks[%d] = %f", nIdxFlag,
    6909           0 :                          adfMasks[nIdxFlag]);
    6910           0 :                 return false;
    6911             :             }
    6912             : 
    6913          10 :             if (bHasFlagValues)
    6914             :             {
    6915          12 :                 m_anValidFlagValues.push_back(
    6916           6 :                     static_cast<uint32_t>(adfValues[nIdxFlag]));
    6917             :             }
    6918             : 
    6919          10 :             if (bHasFlagMasks)
    6920             :             {
    6921          12 :                 m_anValidFlagMasks.push_back(
    6922           6 :                     static_cast<uint32_t>(adfMasks[nIdxFlag]));
    6923             :             }
    6924             :         }
    6925             :     }
    6926             : 
    6927          39 :     return true;
    6928             : }
    6929             : 
    6930             : /************************************************************************/
    6931             : /*                             IRead()                                  */
    6932             : /************************************************************************/
    6933             : 
    6934          48 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6935             :                             const GInt64 *arrayStep,
    6936             :                             const GPtrDiff_t *bufferStride,
    6937             :                             const GDALExtendedDataType &bufferDataType,
    6938             :                             void *pDstBuffer) const
    6939             : {
    6940          48 :     size_t nElts = 1;
    6941          48 :     const size_t nDims = GetDimensionCount();
    6942          96 :     std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
    6943         132 :     for (size_t i = 0; i < nDims; i++)
    6944          84 :         nElts *= count[i];
    6945          48 :     if (nDims > 0)
    6946             :     {
    6947          43 :         tmpBufferStrideVector.back() = 1;
    6948          84 :         for (size_t i = nDims - 1; i > 0;)
    6949             :         {
    6950          41 :             --i;
    6951          41 :             tmpBufferStrideVector[i] =
    6952          41 :                 tmpBufferStrideVector[i + 1] * count[i + 1];
    6953             :         }
    6954             :     }
    6955             : 
    6956             :     /* Optimized case: if we are an integer data type and that there is no */
    6957             :     /* attribute that can be used to set mask = 0, then fill the mask buffer */
    6958             :     /* directly */
    6959          46 :     if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
    6960          70 :         !m_bHasValidMax && m_anValidFlagValues.empty() &&
    6961          32 :         m_anValidFlagMasks.empty() &&
    6962         103 :         m_poParent->GetRawNoDataValue() == nullptr &&
    6963           9 :         GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
    6964             :     {
    6965           7 :         const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
    6966           7 :         if (bBufferDataTypeIsByte)  // Byte case
    6967             :         {
    6968           4 :             bool bContiguous = true;
    6969          10 :             for (size_t i = 0; i < nDims; i++)
    6970             :             {
    6971           7 :                 if (bufferStride[i] != tmpBufferStrideVector[i])
    6972             :                 {
    6973           1 :                     bContiguous = false;
    6974           1 :                     break;
    6975             :                 }
    6976             :             }
    6977           4 :             if (bContiguous)
    6978             :             {
    6979             :                 // CPLDebug("GDAL", "GetMask(): contiguous case");
    6980           3 :                 memset(pDstBuffer, 1, nElts);
    6981           3 :                 return true;
    6982             :             }
    6983             :         }
    6984             : 
    6985             :         struct Stack
    6986             :         {
    6987             :             size_t nIters = 0;
    6988             :             GByte *dst_ptr = nullptr;
    6989             :             GPtrDiff_t dst_inc_offset = 0;
    6990             :         };
    6991             : 
    6992           4 :         std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
    6993           4 :         const size_t nBufferDTSize = bufferDataType.GetSize();
    6994          13 :         for (size_t i = 0; i < nDims; i++)
    6995             :         {
    6996           9 :             stack[i].dst_inc_offset =
    6997           9 :                 static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6998             :         }
    6999           4 :         stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    7000             : 
    7001           4 :         size_t dimIdx = 0;
    7002           4 :         const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
    7003             :         GByte abyOne[16];  // 16 is sizeof GDT_CFloat64
    7004           4 :         CPLAssert(nBufferDTSize <= 16);
    7005           4 :         const GByte flag = 1;
    7006             :         // Coverity misses that m_dt is of type Byte
    7007             :         // coverity[overrun-buffer-val]
    7008           4 :         GDALExtendedDataType::CopyValue(&flag, m_dt, abyOne, bufferDataType);
    7009             : 
    7010          28 :     lbl_next_depth:
    7011          28 :         if (dimIdx == nDimsMinus1)
    7012             :         {
    7013          19 :             auto nIters = nDims > 0 ? count[dimIdx] : 1;
    7014          19 :             GByte *dst_ptr = stack[dimIdx].dst_ptr;
    7015             : 
    7016             :             while (true)
    7017             :             {
    7018             :                 // cppcheck-suppress knownConditionTrueFalse
    7019          73 :                 if (bBufferDataTypeIsByte)
    7020             :                 {
    7021          24 :                     *dst_ptr = flag;
    7022             :                 }
    7023             :                 else
    7024             :                 {
    7025          49 :                     memcpy(dst_ptr, abyOne, nBufferDTSize);
    7026             :                 }
    7027             : 
    7028          73 :                 if ((--nIters) == 0)
    7029          19 :                     break;
    7030          54 :                 dst_ptr += stack[dimIdx].dst_inc_offset;
    7031             :             }
    7032             :         }
    7033             :         else
    7034             :         {
    7035           9 :             stack[dimIdx].nIters = count[dimIdx];
    7036             :             while (true)
    7037             :             {
    7038          24 :                 dimIdx++;
    7039          24 :                 stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    7040          24 :                 goto lbl_next_depth;
    7041          24 :             lbl_return_to_caller:
    7042          24 :                 dimIdx--;
    7043          24 :                 if ((--stack[dimIdx].nIters) == 0)
    7044           9 :                     break;
    7045          15 :                 stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    7046             :             }
    7047             :         }
    7048          28 :         if (dimIdx > 0)
    7049          24 :             goto lbl_return_to_caller;
    7050             : 
    7051           4 :         return true;
    7052             :     }
    7053             : 
    7054             :     const auto oTmpBufferDT =
    7055          41 :         GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
    7056             :             ? GDALExtendedDataType::Create(GDT_Float64)
    7057          82 :             : m_poParent->GetDataType();
    7058          41 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
    7059          41 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
    7060          41 :     if (!pTempBuffer)
    7061           0 :         return false;
    7062          82 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
    7063          41 :                           tmpBufferStrideVector.data(), oTmpBufferDT,
    7064             :                           pTempBuffer))
    7065             :     {
    7066           0 :         VSIFree(pTempBuffer);
    7067           0 :         return false;
    7068             :     }
    7069             : 
    7070          41 :     switch (oTmpBufferDT.GetNumericDataType())
    7071             :     {
    7072           6 :         case GDT_Byte:
    7073           6 :             ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
    7074             :                                 pTempBuffer, oTmpBufferDT,
    7075             :                                 tmpBufferStrideVector);
    7076           6 :             break;
    7077             : 
    7078           0 :         case GDT_Int8:
    7079           0 :             ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
    7080             :                                 pTempBuffer, oTmpBufferDT,
    7081             :                                 tmpBufferStrideVector);
    7082           0 :             break;
    7083             : 
    7084           1 :         case GDT_UInt16:
    7085           1 :             ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
    7086             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
    7087             :                                   tmpBufferStrideVector);
    7088           1 :             break;
    7089             : 
    7090          14 :         case GDT_Int16:
    7091          14 :             ReadInternal<GInt16>(count, bufferStride, bufferDataType,
    7092             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7093             :                                  tmpBufferStrideVector);
    7094          14 :             break;
    7095             : 
    7096           1 :         case GDT_UInt32:
    7097           1 :             ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
    7098             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
    7099             :                                   tmpBufferStrideVector);
    7100           1 :             break;
    7101             : 
    7102           5 :         case GDT_Int32:
    7103           5 :             ReadInternal<GInt32>(count, bufferStride, bufferDataType,
    7104             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7105             :                                  tmpBufferStrideVector);
    7106           5 :             break;
    7107             : 
    7108           0 :         case GDT_UInt64:
    7109           0 :             ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
    7110             :                                         pDstBuffer, pTempBuffer, oTmpBufferDT,
    7111             :                                         tmpBufferStrideVector);
    7112           0 :             break;
    7113             : 
    7114           0 :         case GDT_Int64:
    7115           0 :             ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
    7116             :                                        pDstBuffer, pTempBuffer, oTmpBufferDT,
    7117             :                                        tmpBufferStrideVector);
    7118           0 :             break;
    7119             : 
    7120           7 :         case GDT_Float32:
    7121           7 :             ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
    7122             :                                 pTempBuffer, oTmpBufferDT,
    7123             :                                 tmpBufferStrideVector);
    7124           7 :             break;
    7125             : 
    7126           7 :         case GDT_Float64:
    7127           7 :             ReadInternal<double>(count, bufferStride, bufferDataType,
    7128             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7129             :                                  tmpBufferStrideVector);
    7130           7 :             break;
    7131           0 :         case GDT_Unknown:
    7132             :         case GDT_CInt16:
    7133             :         case GDT_CInt32:
    7134             :         case GDT_CFloat32:
    7135             :         case GDT_CFloat64:
    7136             :         case GDT_TypeCount:
    7137           0 :             CPLAssert(false);
    7138             :             break;
    7139             :     }
    7140             : 
    7141          41 :     VSIFree(pTempBuffer);
    7142             : 
    7143          41 :     return true;
    7144             : }
    7145             : 
    7146             : /************************************************************************/
    7147             : /*                          IsValidForDT()                              */
    7148             : /************************************************************************/
    7149             : 
    7150          38 : template <typename Type> static bool IsValidForDT(double dfVal)
    7151             : {
    7152          38 :     if (std::isnan(dfVal))
    7153           0 :         return false;
    7154          38 :     if (dfVal < static_cast<double>(std::numeric_limits<Type>::lowest()))
    7155           0 :         return false;
    7156          38 :     if (dfVal > static_cast<double>(std::numeric_limits<Type>::max()))
    7157           0 :         return false;
    7158          38 :     return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
    7159             : }
    7160             : 
    7161           9 : template <> bool IsValidForDT<double>(double)
    7162             : {
    7163           9 :     return true;
    7164             : }
    7165             : 
    7166             : /************************************************************************/
    7167             : /*                              IsNan()                                 */
    7168             : /************************************************************************/
    7169             : 
    7170        1038 : template <typename Type> inline bool IsNan(Type)
    7171             : {
    7172        1038 :     return false;
    7173             : }
    7174             : 
    7175          25 : template <> bool IsNan<double>(double val)
    7176             : {
    7177          25 :     return std::isnan(val);
    7178             : }
    7179             : 
    7180          26 : template <> bool IsNan<float>(float val)
    7181             : {
    7182          26 :     return std::isnan(val);
    7183             : }
    7184             : 
    7185             : /************************************************************************/
    7186             : /*                         ReadInternal()                               */
    7187             : /************************************************************************/
    7188             : 
    7189             : template <typename Type>
    7190          41 : void GDALMDArrayMask::ReadInternal(
    7191             :     const size_t *count, const GPtrDiff_t *bufferStride,
    7192             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    7193             :     const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
    7194             :     const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
    7195             : {
    7196          41 :     const size_t nDims = GetDimensionCount();
    7197             : 
    7198         205 :     const auto castValue = [](bool &bHasVal, double dfVal) -> Type
    7199             :     {
    7200         205 :         if (bHasVal)
    7201             :         {
    7202          47 :             if (IsValidForDT<Type>(dfVal))
    7203             :             {
    7204          47 :                 return static_cast<Type>(dfVal);
    7205             :             }
    7206             :             else
    7207             :             {
    7208           0 :                 bHasVal = false;
    7209             :             }
    7210             :         }
    7211         158 :         return 0;
    7212             :     };
    7213             : 
    7214          41 :     const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
    7215          41 :     bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
    7216             :     const Type nNoDataValue =
    7217          41 :         castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
    7218          41 :     bool bHasMissingValue = m_bHasMissingValue;
    7219          41 :     const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
    7220          41 :     bool bHasFillValue = m_bHasFillValue;
    7221          41 :     const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
    7222          41 :     bool bHasValidMin = m_bHasValidMin;
    7223          41 :     const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
    7224          41 :     bool bHasValidMax = m_bHasValidMax;
    7225          41 :     const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
    7226          41 :     const bool bHasValidFlags =
    7227          41 :         !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
    7228             : 
    7229         348 :     const auto IsValidFlag = [this](Type v)
    7230             :     {
    7231          54 :         if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
    7232             :         {
    7233          20 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
    7234             :             {
    7235          12 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
    7236             :                     m_anValidFlagValues[i])
    7237             :                 {
    7238           4 :                     return true;
    7239             :                 }
    7240             :             }
    7241             :         }
    7242          42 :         else if (!m_anValidFlagValues.empty())
    7243             :         {
    7244          49 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
    7245             :             {
    7246          29 :                 if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
    7247             :                 {
    7248           4 :                     return true;
    7249             :                 }
    7250             :             }
    7251             :         }
    7252             :         else /* if( !m_anValidFlagMasks.empty() ) */
    7253             :         {
    7254          31 :             for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
    7255             :             {
    7256          22 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
    7257             :                 {
    7258           9 :                     return true;
    7259             :                 }
    7260             :             }
    7261             :         }
    7262          37 :         return false;
    7263             :     };
    7264             : 
    7265             : #define GET_MASK_FOR_SAMPLE(v)                                                 \
    7266             :     static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
    7267             :                        !(bHasMissingValue && v == nMissingValue) &&            \
    7268             :                        !(bHasFillValue && v == nFillValue) &&                  \
    7269             :                        !(bHasValidMin && v < nValidMin) &&                     \
    7270             :                        !(bHasValidMax && v > nValidMax) &&                     \
    7271             :                        (!bHasValidFlags || IsValidFlag(v)));
    7272             : 
    7273          41 :     const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
    7274             :     /* Optimized case: Byte output and output buffer is contiguous */
    7275          41 :     if (bBufferDataTypeIsByte)
    7276             :     {
    7277          37 :         bool bContiguous = true;
    7278          96 :         for (size_t i = 0; i < nDims; i++)
    7279             :         {
    7280          60 :             if (bufferStride[i] != tmpBufferStrideVector[i])
    7281             :             {
    7282           1 :                 bContiguous = false;
    7283           1 :                 break;
    7284             :             }
    7285             :         }
    7286          37 :         if (bContiguous)
    7287             :         {
    7288          36 :             size_t nElts = 1;
    7289          95 :             for (size_t i = 0; i < nDims; i++)
    7290          59 :                 nElts *= count[i];
    7291             : 
    7292         670 :             for (size_t i = 0; i < nElts; i++)
    7293             :             {
    7294         634 :                 const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
    7295         634 :                 static_cast<GByte *>(pDstBuffer)[i] =
    7296         634 :                     GET_MASK_FOR_SAMPLE(*pSrc);
    7297             :             }
    7298          36 :             return;
    7299             :         }
    7300             :     }
    7301             : 
    7302           5 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
    7303             : 
    7304             :     struct Stack
    7305             :     {
    7306             :         size_t nIters = 0;
    7307             :         const GByte *src_ptr = nullptr;
    7308             :         GByte *dst_ptr = nullptr;
    7309             :         GPtrDiff_t src_inc_offset = 0;
    7310             :         GPtrDiff_t dst_inc_offset = 0;
    7311             :     };
    7312             : 
    7313          10 :     std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
    7314           5 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    7315          15 :     for (size_t i = 0; i < nDims; i++)
    7316             :     {
    7317          20 :         stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
    7318          10 :             tmpBufferStrideVector[i] * nTmpBufferDTSize);
    7319          10 :         stack[i].dst_inc_offset =
    7320          10 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    7321             :     }
    7322           5 :     stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
    7323           5 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    7324             : 
    7325           5 :     size_t dimIdx = 0;
    7326           5 :     const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
    7327             :     GByte abyZeroOrOne[2][16];  // 16 is sizeof GDT_CFloat64
    7328           5 :     CPLAssert(nBufferDTSize <= 16);
    7329          15 :     for (GByte flag = 0; flag <= 1; flag++)
    7330             :     {
    7331             :         // Coverity misses that m_dt is of type Byte
    7332             :         // coverity[overrun-buffer-val]
    7333          10 :         GDALExtendedDataType::CopyValue(&flag, m_dt, abyZeroOrOne[flag],
    7334             :                                         bufferDataType);
    7335             :     }
    7336             : 
    7337          43 : lbl_next_depth:
    7338          43 :     if (dimIdx == nDimsMinus1)
    7339             :     {
    7340          35 :         auto nIters = nDims > 0 ? count[dimIdx] : 1;
    7341          35 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
    7342          35 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
    7343             : 
    7344         420 :         while (true)
    7345             :         {
    7346         455 :             const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
    7347         455 :             const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
    7348             : 
    7349         455 :             if (bBufferDataTypeIsByte)
    7350             :             {
    7351          24 :                 *dst_ptr = flag;
    7352             :             }
    7353             :             else
    7354             :             {
    7355         431 :                 memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
    7356             :             }
    7357             : 
    7358         455 :             if ((--nIters) == 0)
    7359          35 :                 break;
    7360         420 :             src_ptr += stack[dimIdx].src_inc_offset;
    7361         420 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    7362             :         }
    7363             :     }
    7364             :     else
    7365             :     {
    7366           8 :         stack[dimIdx].nIters = count[dimIdx];
    7367             :         while (true)
    7368             :         {
    7369          38 :             dimIdx++;
    7370          38 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    7371          38 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    7372          38 :             goto lbl_next_depth;
    7373          38 :         lbl_return_to_caller:
    7374          38 :             dimIdx--;
    7375          38 :             if ((--stack[dimIdx].nIters) == 0)
    7376           8 :                 break;
    7377          30 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    7378          30 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    7379             :         }
    7380             :     }
    7381          43 :     if (dimIdx > 0)
    7382          38 :         goto lbl_return_to_caller;
    7383             : }
    7384             : 
    7385             : /************************************************************************/
    7386             : /*                            GetMask()                                 */
    7387             : /************************************************************************/
    7388             : 
    7389             : /** Return an array that is a mask for the current array
    7390             : 
    7391             :  This array will be of type Byte, with values set to 0 to indicate invalid
    7392             :  pixels of the current array, and values set to 1 to indicate valid pixels.
    7393             : 
    7394             :  The generic implementation honours the NoDataValue, as well as various
    7395             :  netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
    7396             :  and valid_range.
    7397             : 
    7398             :  Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
    7399             :  can be used to specify strings of the "flag_meanings" attribute
    7400             :  (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
    7401             :  for which pixels matching any of those flags will be set at 1 in the mask array,
    7402             :  and pixels matching none of those flags will be set at 0.
    7403             :  For example, let's consider the following netCDF variable defined with:
    7404             :  \verbatim
    7405             :  l2p_flags:valid_min = 0s ;
    7406             :  l2p_flags:valid_max = 256s ;
    7407             :  l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
    7408             :  l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
    7409             :  \endverbatim
    7410             : 
    7411             :  GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
    7412             :  - for pixel values *outside* valid_range [0,256], the mask value will be 0.
    7413             :  - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
    7414             :    will be 1.
    7415             :  - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
    7416             :    will be 0.
    7417             : 
    7418             :  This is the same as the C function GDALMDArrayGetMask().
    7419             : 
    7420             :  @param papszOptions NULL-terminated list of options, or NULL.
    7421             : 
    7422             :  @return a new array, that holds a reference to the original one, and thus is
    7423             :  a view of it (not a copy), or nullptr in case of error.
    7424             : */
    7425             : std::shared_ptr<GDALMDArray>
    7426          46 : GDALMDArray::GetMask(CSLConstList papszOptions) const
    7427             : {
    7428          92 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    7429          46 :     if (!self)
    7430             :     {
    7431           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    7432             :                  "Driver implementation issue: m_pSelf not set !");
    7433           0 :         return nullptr;
    7434             :     }
    7435          46 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    7436             :     {
    7437           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    7438             :                  "GetMask() only supports numeric data type");
    7439           1 :         return nullptr;
    7440             :     }
    7441          45 :     return GDALMDArrayMask::Create(self, papszOptions);
    7442             : }
    7443             : 
    7444             : /************************************************************************/
    7445             : /*                         IsRegularlySpaced()                          */
    7446             : /************************************************************************/
    7447             : 
    7448             : /** Returns whether an array is a 1D regularly spaced array.
    7449             :  *
    7450             :  * @param[out] dfStart     First value in the array
    7451             :  * @param[out] dfIncrement Increment/spacing between consecutive values.
    7452             :  * @return true if the array is regularly spaced.
    7453             :  */
    7454         181 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
    7455             : {
    7456         181 :     dfStart = 0;
    7457         181 :     dfIncrement = 0;
    7458         181 :     if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
    7459           0 :         return false;
    7460         181 :     const auto nSize = GetDimensions()[0]->GetSize();
    7461         181 :     if (nSize <= 1 || nSize > 10 * 1000 * 1000)
    7462           2 :         return false;
    7463             : 
    7464         179 :     size_t nCount = static_cast<size_t>(nSize);
    7465         358 :     std::vector<double> adfTmp;
    7466             :     try
    7467             :     {
    7468         179 :         adfTmp.resize(nCount);
    7469             :     }
    7470           0 :     catch (const std::exception &)
    7471             :     {
    7472           0 :         return false;
    7473             :     }
    7474             : 
    7475         179 :     GUInt64 anStart[1] = {0};
    7476         179 :     size_t anCount[1] = {nCount};
    7477             : 
    7478             :     const auto IsRegularlySpacedInternal =
    7479       82516 :         [&dfStart, &dfIncrement, &anCount, &adfTmp]()
    7480             :     {
    7481         251 :         dfStart = adfTmp[0];
    7482         251 :         dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
    7483         251 :         if (dfIncrement == 0)
    7484             :         {
    7485           3 :             return false;
    7486             :         }
    7487       20564 :         for (size_t i = 1; i < anCount[0]; i++)
    7488             :         {
    7489       20316 :             if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
    7490       20316 :                 1e-3 * fabs(dfIncrement))
    7491             :             {
    7492           0 :                 return false;
    7493             :             }
    7494             :         }
    7495         248 :         return true;
    7496         179 :     };
    7497             : 
    7498             :     // First try with the first block(s). This can avoid excessive processing
    7499             :     // time, for example with Zarr datasets.
    7500             :     // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
    7501             :     // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
    7502         179 :     const auto nBlockSize = GetBlockSize()[0];
    7503         179 :     if (nCount >= 5 && nBlockSize <= nCount / 2)
    7504             :     {
    7505             :         size_t nReducedCount =
    7506          75 :             std::max<size_t>(3, static_cast<size_t>(nBlockSize));
    7507         436 :         while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
    7508         361 :             nReducedCount *= 2;
    7509          75 :         anCount[0] = nReducedCount;
    7510          75 :         if (!Read(anStart, anCount, nullptr, nullptr,
    7511         150 :                   GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
    7512             :         {
    7513           0 :             return false;
    7514             :         }
    7515          75 :         if (!IsRegularlySpacedInternal())
    7516             :         {
    7517           3 :             return false;
    7518             :         }
    7519             : 
    7520             :         // Get next values
    7521          72 :         anStart[0] = nReducedCount;
    7522          72 :         anCount[0] = nCount - nReducedCount;
    7523             :     }
    7524             : 
    7525         176 :     if (!Read(anStart, anCount, nullptr, nullptr,
    7526         352 :               GDALExtendedDataType::Create(GDT_Float64),
    7527         176 :               &adfTmp[static_cast<size_t>(anStart[0])]))
    7528             :     {
    7529           0 :         return false;
    7530             :     }
    7531             : 
    7532         176 :     return IsRegularlySpacedInternal();
    7533             : }
    7534             : 
    7535             : /************************************************************************/
    7536             : /*                         GuessGeoTransform()                          */
    7537             : /************************************************************************/
    7538             : 
    7539             : /** Returns whether 2 specified dimensions form a geotransform
    7540             :  *
    7541             :  * @param nDimX                Index of the X axis.
    7542             :  * @param nDimY                Index of the Y axis.
    7543             :  * @param bPixelIsPoint        Whether the geotransform should be returned
    7544             :  *                             with the pixel-is-point (pixel-center) convention
    7545             :  *                             (bPixelIsPoint = true), or with the pixel-is-area
    7546             :  *                             (top left corner convention)
    7547             :  *                             (bPixelIsPoint = false)
    7548             :  * @param[out] adfGeoTransform Computed geotransform
    7549             :  * @return true if a geotransform could be computed.
    7550             :  */
    7551         207 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
    7552             :                                     bool bPixelIsPoint,
    7553             :                                     double adfGeoTransform[6]) const
    7554             : {
    7555         207 :     const auto &dims(GetDimensions());
    7556         414 :     auto poVarX = dims[nDimX]->GetIndexingVariable();
    7557         414 :     auto poVarY = dims[nDimY]->GetIndexingVariable();
    7558         207 :     double dfXStart = 0.0;
    7559         207 :     double dfXSpacing = 0.0;
    7560         207 :     double dfYStart = 0.0;
    7561         207 :     double dfYSpacing = 0.0;
    7562         477 :     if (poVarX && poVarX->GetDimensionCount() == 1 &&
    7563         270 :         poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
    7564         317 :         poVarY && poVarY->GetDimensionCount() == 1 &&
    7565          91 :         poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
    7566         428 :         poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
    7567          86 :         poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
    7568             :     {
    7569          86 :         adfGeoTransform[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
    7570          86 :         adfGeoTransform[1] = dfXSpacing;
    7571          86 :         adfGeoTransform[2] = 0;
    7572          86 :         adfGeoTransform[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
    7573          86 :         adfGeoTransform[4] = 0;
    7574          86 :         adfGeoTransform[5] = dfYSpacing;
    7575          86 :         return true;
    7576             :     }
    7577         121 :     return false;
    7578             : }
    7579             : 
    7580             : /************************************************************************/
    7581             : /*                       GDALMDArrayResampled                           */
    7582             : /************************************************************************/
    7583             : 
    7584             : class GDALMDArrayResampledDataset;
    7585             : 
    7586             : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
    7587             : {
    7588             :   protected:
    7589             :     CPLErr IReadBlock(int, int, void *) override;
    7590             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
    7591             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
    7592             :                      GDALDataType eBufType, GSpacing nPixelSpaceBuf,
    7593             :                      GSpacing nLineSpaceBuf,
    7594             :                      GDALRasterIOExtraArg *psExtraArg) override;
    7595             : 
    7596             :   public:
    7597             :     explicit GDALMDArrayResampledDatasetRasterBand(
    7598             :         GDALMDArrayResampledDataset *poDSIn);
    7599             : 
    7600             :     double GetNoDataValue(int *pbHasNoData) override;
    7601             : };
    7602             : 
    7603             : class GDALMDArrayResampledDataset final : public GDALPamDataset
    7604             : {
    7605             :     friend class GDALMDArrayResampled;
    7606             :     friend class GDALMDArrayResampledDatasetRasterBand;
    7607             : 
    7608             :     std::shared_ptr<GDALMDArray> m_poArray;
    7609             :     const size_t m_iXDim;
    7610             :     const size_t m_iYDim;
    7611             :     double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
    7612             :     bool m_bHasGT = false;
    7613             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
    7614             : 
    7615             :     std::vector<GUInt64> m_anOffset{};
    7616             :     std::vector<size_t> m_anCount{};
    7617             :     std::vector<GPtrDiff_t> m_anStride{};
    7618             : 
    7619             :     std::string m_osFilenameLong{};
    7620             :     std::string m_osFilenameLat{};
    7621             : 
    7622             :   public:
    7623          24 :     GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
    7624             :                                 size_t iXDim, size_t iYDim)
    7625          24 :         : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
    7626          24 :           m_anOffset(m_poArray->GetDimensionCount(), 0),
    7627          24 :           m_anCount(m_poArray->GetDimensionCount(), 1),
    7628          72 :           m_anStride(m_poArray->GetDimensionCount(), 0)
    7629             :     {
    7630          24 :         const auto &dims(m_poArray->GetDimensions());
    7631             : 
    7632          24 :         nRasterYSize = static_cast<int>(
    7633          24 :             std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
    7634          24 :         nRasterXSize = static_cast<int>(
    7635          24 :             std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
    7636             : 
    7637          24 :         m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false,
    7638          24 :                                                 m_adfGeoTransform);
    7639             : 
    7640          24 :         SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
    7641          24 :     }
    7642             : 
    7643          48 :     ~GDALMDArrayResampledDataset()
    7644          24 :     {
    7645          24 :         if (!m_osFilenameLong.empty())
    7646           5 :             VSIUnlink(m_osFilenameLong.c_str());
    7647          24 :         if (!m_osFilenameLat.empty())
    7648           5 :             VSIUnlink(m_osFilenameLat.c_str());
    7649          48 :     }
    7650             : 
    7651          43 :     CPLErr GetGeoTransform(double *padfGeoTransform) override
    7652             :     {
    7653          43 :         memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
    7654          43 :         return m_bHasGT ? CE_None : CE_Failure;
    7655             :     }
    7656             : 
    7657         105 :     const OGRSpatialReference *GetSpatialRef() const override
    7658             :     {
    7659         105 :         m_poSRS = m_poArray->GetSpatialRef();
    7660         105 :         if (m_poSRS)
    7661             :         {
    7662          79 :             m_poSRS.reset(m_poSRS->Clone());
    7663         158 :             auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
    7664         237 :             for (auto &m : axisMapping)
    7665             :             {
    7666         158 :                 if (m == static_cast<int>(m_iXDim) + 1)
    7667          79 :                     m = 1;
    7668          79 :                 else if (m == static_cast<int>(m_iYDim) + 1)
    7669          79 :                     m = 2;
    7670             :             }
    7671          79 :             m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
    7672             :         }
    7673         105 :         return m_poSRS.get();
    7674             :     }
    7675             : 
    7676           5 :     void SetGeolocationArray(const std::string &osFilenameLong,
    7677             :                              const std::string &osFilenameLat)
    7678             :     {
    7679           5 :         m_osFilenameLong = osFilenameLong;
    7680           5 :         m_osFilenameLat = osFilenameLat;
    7681          10 :         CPLStringList aosGeoLoc;
    7682           5 :         aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
    7683           5 :         aosGeoLoc.SetNameValue("LINE_STEP", "1");
    7684           5 :         aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
    7685           5 :         aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
    7686           5 :         aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG);  // FIXME?
    7687           5 :         aosGeoLoc.SetNameValue("X_BAND", "1");
    7688           5 :         aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
    7689           5 :         aosGeoLoc.SetNameValue("Y_BAND", "1");
    7690           5 :         aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
    7691           5 :         aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
    7692           5 :         SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
    7693           5 :     }
    7694             : };
    7695             : 
    7696             : /************************************************************************/
    7697             : /*                   GDALMDArrayResampledDatasetRasterBand()            */
    7698             : /************************************************************************/
    7699             : 
    7700          24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
    7701          24 :     GDALMDArrayResampledDataset *poDSIn)
    7702             : {
    7703          24 :     const auto &poArray(poDSIn->m_poArray);
    7704          24 :     const auto blockSize(poArray->GetBlockSize());
    7705          24 :     nBlockYSize = (blockSize[poDSIn->m_iYDim])
    7706          24 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    7707          13 :                                                   blockSize[poDSIn->m_iYDim]))
    7708          24 :                       : 1;
    7709          24 :     nBlockXSize = blockSize[poDSIn->m_iXDim]
    7710          13 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    7711          13 :                                                   blockSize[poDSIn->m_iXDim]))
    7712          24 :                       : poDSIn->GetRasterXSize();
    7713          24 :     eDataType = poArray->GetDataType().GetNumericDataType();
    7714          24 :     eAccess = poDSIn->eAccess;
    7715          24 : }
    7716             : 
    7717             : /************************************************************************/
    7718             : /*                           GetNoDataValue()                           */
    7719             : /************************************************************************/
    7720             : 
    7721          50 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
    7722             : {
    7723          50 :     auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
    7724          50 :     const auto &poArray(l_poDS->m_poArray);
    7725          50 :     bool bHasNodata = false;
    7726          50 :     double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
    7727          50 :     if (pbHasNoData)
    7728          46 :         *pbHasNoData = bHasNodata;
    7729          50 :     return dfRes;
    7730             : }
    7731             : 
    7732             : /************************************************************************/
    7733             : /*                            IReadBlock()                              */
    7734             : /************************************************************************/
    7735             : 
    7736           0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
    7737             :                                                          int nBlockYOff,
    7738             :                                                          void *pImage)
    7739             : {
    7740           0 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    7741           0 :     const int nXOff = nBlockXOff * nBlockXSize;
    7742           0 :     const int nYOff = nBlockYOff * nBlockYSize;
    7743           0 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    7744           0 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    7745             :     GDALRasterIOExtraArg sExtraArg;
    7746           0 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    7747           0 :     return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    7748             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    7749           0 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    7750             : }
    7751             : 
    7752             : /************************************************************************/
    7753             : /*                            IRasterIO()                               */
    7754             : /************************************************************************/
    7755             : 
    7756          32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
    7757             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
    7758             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
    7759             :     GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
    7760             :     GDALRasterIOExtraArg *psExtraArg)
    7761             : {
    7762          32 :     auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
    7763          32 :     const auto &poArray(l_poDS->m_poArray);
    7764          32 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
    7765          32 :     if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
    7766          32 :         nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
    7767          32 :         (nLineSpaceBuf % nBufferDTSize) == 0)
    7768             :     {
    7769          32 :         l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
    7770          32 :         l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
    7771          64 :         l_poDS->m_anStride[l_poDS->m_iXDim] =
    7772          32 :             static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
    7773             : 
    7774          32 :         l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
    7775          32 :         l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
    7776          64 :         l_poDS->m_anStride[l_poDS->m_iYDim] =
    7777          32 :             static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
    7778             : 
    7779          64 :         return poArray->Read(l_poDS->m_anOffset.data(),
    7780          32 :                              l_poDS->m_anCount.data(), nullptr,
    7781          32 :                              l_poDS->m_anStride.data(),
    7782          64 :                              GDALExtendedDataType::Create(eBufType), pData)
    7783          32 :                    ? CE_None
    7784          32 :                    : CE_Failure;
    7785             :     }
    7786           0 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    7787             :                                      pData, nBufXSize, nBufYSize, eBufType,
    7788           0 :                                      nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
    7789             : }
    7790             : 
    7791             : class GDALMDArrayResampled final : public GDALPamMDArray
    7792             : {
    7793             :   private:
    7794             :     std::shared_ptr<GDALMDArray> m_poParent{};
    7795             :     std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
    7796             :     std::vector<GUInt64> m_anBlockSize;
    7797             :     GDALExtendedDataType m_dt;
    7798             :     std::shared_ptr<OGRSpatialReference> m_poSRS{};
    7799             :     std::shared_ptr<GDALMDArray> m_poVarX{};
    7800             :     std::shared_ptr<GDALMDArray> m_poVarY{};
    7801             :     std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
    7802             :     std::unique_ptr<GDALDataset> m_poReprojectedDS{};
    7803             : 
    7804             :   protected:
    7805          21 :     GDALMDArrayResampled(
    7806             :         const std::shared_ptr<GDALMDArray> &poParent,
    7807             :         const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
    7808             :         const std::vector<GUInt64> &anBlockSize)
    7809          42 :         : GDALAbstractMDArray(std::string(),
    7810          42 :                               "Resampled view of " + poParent->GetFullName()),
    7811             :           GDALPamMDArray(
    7812          42 :               std::string(), "Resampled view of " + poParent->GetFullName(),
    7813          42 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
    7814          21 :           m_poParent(std::move(poParent)), m_apoDims(apoDims),
    7815         105 :           m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
    7816             :     {
    7817          21 :         CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
    7818          21 :         CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
    7819          21 :     }
    7820             : 
    7821             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    7822             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    7823             :                const GDALExtendedDataType &bufferDataType,
    7824             :                void *pDstBuffer) const override;
    7825             : 
    7826             :   public:
    7827             :     static std::shared_ptr<GDALMDArray>
    7828             :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    7829             :            const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
    7830             :            GDALRIOResampleAlg resampleAlg,
    7831             :            const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
    7832             : 
    7833          42 :     ~GDALMDArrayResampled()
    7834          21 :     {
    7835             :         // First close the warped VRT
    7836          21 :         m_poReprojectedDS.reset();
    7837          21 :         m_poParentDS.reset();
    7838          42 :     }
    7839             : 
    7840          11 :     bool IsWritable() const override
    7841             :     {
    7842          11 :         return false;
    7843             :     }
    7844             : 
    7845          74 :     const std::string &GetFilename() const override
    7846             :     {
    7847          74 :         return m_poParent->GetFilename();
    7848             :     }
    7849             : 
    7850             :     const std::vector<std::shared_ptr<GDALDimension>> &
    7851         257 :     GetDimensions() const override
    7852             :     {
    7853         257 :         return m_apoDims;
    7854             :     }
    7855             : 
    7856         109 :     const GDALExtendedDataType &GetDataType() const override
    7857             :     {
    7858         109 :         return m_dt;
    7859             :     }
    7860             : 
    7861          21 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    7862             :     {
    7863          21 :         return m_poSRS;
    7864             :     }
    7865             : 
    7866          12 :     std::vector<GUInt64> GetBlockSize() const override
    7867             :     {
    7868          12 :         return m_anBlockSize;
    7869             :     }
    7870             : 
    7871             :     std::shared_ptr<GDALAttribute>
    7872           1 :     GetAttribute(const std::string &osName) const override
    7873             :     {
    7874           1 :         return m_poParent->GetAttribute(osName);
    7875             :     }
    7876             : 
    7877             :     std::vector<std::shared_ptr<GDALAttribute>>
    7878          12 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    7879             :     {
    7880          12 :         return m_poParent->GetAttributes(papszOptions);
    7881             :     }
    7882             : 
    7883           1 :     const std::string &GetUnit() const override
    7884             :     {
    7885           1 :         return m_poParent->GetUnit();
    7886             :     }
    7887             : 
    7888           1 :     const void *GetRawNoDataValue() const override
    7889             :     {
    7890           1 :         return m_poParent->GetRawNoDataValue();
    7891             :     }
    7892             : 
    7893           1 :     double GetOffset(bool *pbHasOffset,
    7894             :                      GDALDataType *peStorageType) const override
    7895             :     {
    7896           1 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    7897             :     }
    7898             : 
    7899           1 :     double GetScale(bool *pbHasScale,
    7900             :                     GDALDataType *peStorageType) const override
    7901             :     {
    7902           1 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    7903             :     }
    7904             : };
    7905             : 
    7906             : /************************************************************************/
    7907             : /*                   GDALMDArrayResampled::Create()                     */
    7908             : /************************************************************************/
    7909             : 
    7910          29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
    7911             :     const std::shared_ptr<GDALMDArray> &poParent,
    7912             :     const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
    7913             :     GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
    7914             :     CSLConstList /* papszOptions */)
    7915             : {
    7916          29 :     const char *pszResampleAlg = "nearest";
    7917          29 :     bool unsupported = false;
    7918          29 :     switch (resampleAlg)
    7919             :     {
    7920          16 :         case GRIORA_NearestNeighbour:
    7921          16 :             pszResampleAlg = "nearest";
    7922          16 :             break;
    7923           2 :         case GRIORA_Bilinear:
    7924           2 :             pszResampleAlg = "bilinear";
    7925           2 :             break;
    7926           5 :         case GRIORA_Cubic:
    7927           5 :             pszResampleAlg = "cubic";
    7928           5 :             break;
    7929           1 :         case GRIORA_CubicSpline:
    7930           1 :             pszResampleAlg = "cubicspline";
    7931           1 :             break;
    7932           1 :         case GRIORA_Lanczos:
    7933           1 :             pszResampleAlg = "lanczos";
    7934           1 :             break;
    7935           1 :         case GRIORA_Average:
    7936           1 :             pszResampleAlg = "average";
    7937           1 :             break;
    7938           1 :         case GRIORA_Mode:
    7939           1 :             pszResampleAlg = "mode";
    7940           1 :             break;
    7941           1 :         case GRIORA_Gauss:
    7942           1 :             unsupported = true;
    7943           1 :             break;
    7944           0 :         case GRIORA_RESERVED_START:
    7945           0 :             unsupported = true;
    7946           0 :             break;
    7947           0 :         case GRIORA_RESERVED_END:
    7948           0 :             unsupported = true;
    7949           0 :             break;
    7950           1 :         case GRIORA_RMS:
    7951           1 :             pszResampleAlg = "rms";
    7952           1 :             break;
    7953             :     }
    7954          29 :     if (unsupported)
    7955             :     {
    7956           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    7957             :                  "Unsupported resample method for GetResampled()");
    7958           1 :         return nullptr;
    7959             :     }
    7960             : 
    7961          28 :     if (poParent->GetDimensionCount() < 2)
    7962             :     {
    7963           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    7964             :                  "GetResampled() only supports 2 dimensions or more");
    7965           1 :         return nullptr;
    7966             :     }
    7967             : 
    7968          27 :     const auto &aoParentDims = poParent->GetDimensions();
    7969          27 :     if (apoNewDimsIn.size() != aoParentDims.size())
    7970             :     {
    7971           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    7972             :                  "GetResampled(): apoNewDims size should be the same as "
    7973             :                  "GetDimensionCount()");
    7974           2 :         return nullptr;
    7975             :     }
    7976             : 
    7977          50 :     std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
    7978          25 :     apoNewDims.reserve(apoNewDimsIn.size());
    7979             : 
    7980          50 :     std::vector<GUInt64> anBlockSize;
    7981          25 :     anBlockSize.reserve(apoNewDimsIn.size());
    7982          50 :     const auto &anParentBlockSize = poParent->GetBlockSize();
    7983             : 
    7984          50 :     auto apoParentDims = poParent->GetDimensions();
    7985             :     // Special case for NASA EMIT datasets
    7986          30 :     const bool bYXBandOrder = (apoParentDims.size() == 3 &&
    7987           7 :                                apoParentDims[0]->GetName() == "downtrack" &&
    7988          32 :                                apoParentDims[1]->GetName() == "crosstrack" &&
    7989           2 :                                apoParentDims[2]->GetName() == "bands");
    7990             : 
    7991             :     const size_t iYDimParent =
    7992          25 :         bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
    7993             :     const size_t iXDimParent =
    7994          25 :         bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
    7995             : 
    7996          77 :     for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
    7997             :     {
    7998          53 :         if (i == iYDimParent || i == iXDimParent)
    7999          48 :             continue;
    8000           5 :         if (apoNewDimsIn[i] == nullptr)
    8001             :         {
    8002           3 :             apoNewDims.emplace_back(aoParentDims[i]);
    8003             :         }
    8004           3 :         else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
    8005           1 :                  apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
    8006             :         {
    8007           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    8008             :                      "GetResampled(): apoNewDims[%u] should be the same "
    8009             :                      "as its parent",
    8010             :                      i);
    8011           1 :             return nullptr;
    8012             :         }
    8013             :         else
    8014             :         {
    8015           1 :             apoNewDims.emplace_back(aoParentDims[i]);
    8016             :         }
    8017           4 :         anBlockSize.emplace_back(anParentBlockSize[i]);
    8018             :     }
    8019             : 
    8020             :     std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
    8021          48 :         new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
    8022             : 
    8023          24 :     double dfXStart = 0.0;
    8024          24 :     double dfXSpacing = 0.0;
    8025          24 :     bool gotXSpacing = false;
    8026          48 :     auto poNewDimX = apoNewDimsIn[iXDimParent];
    8027          24 :     if (poNewDimX)
    8028             :     {
    8029           4 :         if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
    8030             :         {
    8031           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    8032             :                      "Too big size for X dimension");
    8033           0 :             return nullptr;
    8034             :         }
    8035           4 :         auto var = poNewDimX->GetIndexingVariable();
    8036           4 :         if (var)
    8037             :         {
    8038           2 :             if (var->GetDimensionCount() != 1 ||
    8039           2 :                 var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
    8040           1 :                 !var->IsRegularlySpaced(dfXStart, dfXSpacing))
    8041             :             {
    8042           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    8043             :                          "New X dimension should be indexed by a regularly "
    8044             :                          "spaced variable");
    8045           0 :                 return nullptr;
    8046             :             }
    8047           1 :             gotXSpacing = true;
    8048             :         }
    8049             :     }
    8050             : 
    8051          24 :     double dfYStart = 0.0;
    8052          24 :     double dfYSpacing = 0.0;
    8053          48 :     auto poNewDimY = apoNewDimsIn[iYDimParent];
    8054          24 :     bool gotYSpacing = false;
    8055          24 :     if (poNewDimY)
    8056             :     {
    8057           4 :         if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
    8058             :         {
    8059           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    8060             :                      "Too big size for Y dimension");
    8061           0 :             return nullptr;
    8062             :         }
    8063           4 :         auto var = poNewDimY->GetIndexingVariable();
    8064           4 :         if (var)
    8065             :         {
    8066           2 :             if (var->GetDimensionCount() != 1 ||
    8067           2 :                 var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
    8068           1 :                 !var->IsRegularlySpaced(dfYStart, dfYSpacing))
    8069             :             {
    8070           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    8071             :                          "New Y dimension should be indexed by a regularly "
    8072             :                          "spaced variable");
    8073           0 :                 return nullptr;
    8074             :             }
    8075           1 :             gotYSpacing = true;
    8076             :         }
    8077             :     }
    8078             : 
    8079             :     // This limitation could probably be removed
    8080          24 :     if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
    8081             :     {
    8082           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    8083             :                  "Either none of new X or Y dimension should have an indexing "
    8084             :                  "variable, or both should both should have one.");
    8085           0 :         return nullptr;
    8086             :     }
    8087             : 
    8088          48 :     std::string osDstWKT;
    8089          24 :     if (poTargetSRS)
    8090             :     {
    8091           2 :         char *pszDstWKT = nullptr;
    8092           2 :         if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
    8093             :         {
    8094           0 :             CPLFree(pszDstWKT);
    8095           0 :             return nullptr;
    8096             :         }
    8097           2 :         osDstWKT = pszDstWKT;
    8098           2 :         CPLFree(pszDstWKT);
    8099             :     }
    8100             : 
    8101             :     // Use coordinate variables for geolocation array
    8102          48 :     const auto apoCoordinateVars = poParent->GetCoordinateVariables();
    8103          24 :     bool useGeolocationArray = false;
    8104          24 :     if (apoCoordinateVars.size() >= 2)
    8105             :     {
    8106           0 :         std::shared_ptr<GDALMDArray> poLongVar;
    8107           0 :         std::shared_ptr<GDALMDArray> poLatVar;
    8108          15 :         for (const auto &poCoordVar : apoCoordinateVars)
    8109             :         {
    8110          10 :             const auto &osName = poCoordVar->GetName();
    8111          30 :             const auto poAttr = poCoordVar->GetAttribute("standard_name");
    8112          20 :             std::string osStandardName;
    8113          12 :             if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
    8114           2 :                 poAttr->GetDimensionCount() == 0)
    8115             :             {
    8116           2 :                 const char *pszStandardName = poAttr->ReadAsString();
    8117           2 :                 if (pszStandardName)
    8118           2 :                     osStandardName = pszStandardName;
    8119             :             }
    8120          21 :             if (osName == "lon" || osName == "longitude" ||
    8121          21 :                 osName == "Longitude" || osStandardName == "longitude")
    8122             :             {
    8123           5 :                 poLongVar = poCoordVar;
    8124             :             }
    8125           6 :             else if (osName == "lat" || osName == "latitude" ||
    8126           6 :                      osName == "Latitude" || osStandardName == "latitude")
    8127             :             {
    8128           5 :                 poLatVar = poCoordVar;
    8129             :             }
    8130             :         }
    8131           5 :         if (poLatVar != nullptr && poLongVar != nullptr)
    8132             :         {
    8133           5 :             const auto longDimCount = poLongVar->GetDimensionCount();
    8134           5 :             const auto &longDims = poLongVar->GetDimensions();
    8135           5 :             const auto latDimCount = poLatVar->GetDimensionCount();
    8136           5 :             const auto &latDims = poLatVar->GetDimensions();
    8137           5 :             const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
    8138           5 :             const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
    8139           0 :             if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
    8140           5 :                 latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
    8141             :             {
    8142             :                 // Geolocation arrays are 1D, and of consistent size with
    8143             :                 // the variable
    8144           0 :                 useGeolocationArray = true;
    8145             :             }
    8146           1 :             else if ((longDimCount == 2 ||
    8147           6 :                       (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
    8148          10 :                      longDims[longDimCount - 2]->GetSize() == yDimSize &&
    8149          10 :                      longDims[longDimCount - 1]->GetSize() == xDimSize &&
    8150           1 :                      (latDimCount == 2 ||
    8151           6 :                       (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
    8152          15 :                      latDims[latDimCount - 2]->GetSize() == yDimSize &&
    8153           5 :                      latDims[latDimCount - 1]->GetSize() == xDimSize)
    8154             : 
    8155             :             {
    8156             :                 // Geolocation arrays are 2D (or 3D with first dimension of
    8157             :                 // size 1, as found in Sentinel 5P products), and of consistent
    8158             :                 // size with the variable
    8159           5 :                 useGeolocationArray = true;
    8160             :             }
    8161             :             else
    8162             :             {
    8163           0 :                 CPLDebug(
    8164             :                     "GDAL",
    8165             :                     "Longitude and latitude coordinate variables found, "
    8166             :                     "but their characteristics are not compatible of using "
    8167             :                     "them as geolocation arrays");
    8168             :             }
    8169           5 :             if (useGeolocationArray)
    8170             :             {
    8171          10 :                 CPLDebug("GDAL",
    8172             :                          "Setting geolocation array from variables %s and %s",
    8173           5 :                          poLongVar->GetName().c_str(),
    8174           5 :                          poLatVar->GetName().c_str());
    8175             :                 const std::string osFilenameLong =
    8176           5 :                     VSIMemGenerateHiddenFilename("longitude.tif");
    8177             :                 const std::string osFilenameLat =
    8178           5 :                     VSIMemGenerateHiddenFilename("latitude.tif");
    8179             :                 std::unique_ptr<GDALDataset> poTmpLongDS(
    8180             :                     longDimCount == 1
    8181           0 :                         ? poLongVar->AsClassicDataset(0, 0)
    8182          20 :                         : poLongVar->AsClassicDataset(longDimCount - 1,
    8183          15 :                                                       longDimCount - 2));
    8184           5 :                 auto hTIFFLongDS = GDALTranslate(
    8185             :                     osFilenameLong.c_str(),
    8186             :                     GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
    8187             :                 std::unique_ptr<GDALDataset> poTmpLatDS(
    8188           0 :                     latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
    8189          20 :                                      : poLatVar->AsClassicDataset(
    8190          15 :                                            latDimCount - 1, latDimCount - 2));
    8191           5 :                 auto hTIFFLatDS = GDALTranslate(
    8192             :                     osFilenameLat.c_str(),
    8193             :                     GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
    8194           5 :                 const bool bError =
    8195           5 :                     (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
    8196           5 :                 GDALClose(hTIFFLongDS);
    8197           5 :                 GDALClose(hTIFFLatDS);
    8198           5 :                 if (bError)
    8199             :                 {
    8200           0 :                     VSIUnlink(osFilenameLong.c_str());
    8201           0 :                     VSIUnlink(osFilenameLat.c_str());
    8202           0 :                     return nullptr;
    8203             :                 }
    8204             : 
    8205           5 :                 poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
    8206             :             }
    8207             :         }
    8208             :         else
    8209             :         {
    8210           0 :             CPLDebug("GDAL",
    8211             :                      "Coordinate variables available for %s, but "
    8212             :                      "longitude and/or latitude variables were not identified",
    8213           0 :                      poParent->GetName().c_str());
    8214             :         }
    8215             :     }
    8216             : 
    8217             :     // Build gdalwarp arguments
    8218          48 :     CPLStringList aosArgv;
    8219             : 
    8220          24 :     aosArgv.AddString("-of");
    8221          24 :     aosArgv.AddString("VRT");
    8222             : 
    8223          24 :     aosArgv.AddString("-r");
    8224          24 :     aosArgv.AddString(pszResampleAlg);
    8225             : 
    8226          24 :     if (!osDstWKT.empty())
    8227             :     {
    8228           2 :         aosArgv.AddString("-t_srs");
    8229           2 :         aosArgv.AddString(osDstWKT.c_str());
    8230             :     }
    8231             : 
    8232          24 :     if (useGeolocationArray)
    8233           5 :         aosArgv.AddString("-geoloc");
    8234             : 
    8235          24 :     if (gotXSpacing && gotYSpacing)
    8236             :     {
    8237           1 :         const double dfXMin = dfXStart - dfXSpacing / 2;
    8238             :         const double dfXMax =
    8239           1 :             dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
    8240           1 :         const double dfYMax = dfYStart - dfYSpacing / 2;
    8241             :         const double dfYMin =
    8242           1 :             dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
    8243           1 :         aosArgv.AddString("-te");
    8244           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
    8245           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
    8246           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
    8247           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
    8248             :     }
    8249             : 
    8250          24 :     if (poNewDimX && poNewDimY)
    8251             :     {
    8252           3 :         aosArgv.AddString("-ts");
    8253             :         aosArgv.AddString(
    8254           3 :             CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
    8255             :         aosArgv.AddString(
    8256           3 :             CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
    8257             :     }
    8258          21 :     else if (poNewDimX)
    8259             :     {
    8260           1 :         aosArgv.AddString("-ts");
    8261             :         aosArgv.AddString(
    8262           1 :             CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
    8263           1 :         aosArgv.AddString("0");
    8264             :     }
    8265          20 :     else if (poNewDimY)
    8266             :     {
    8267           1 :         aosArgv.AddString("-ts");
    8268           1 :         aosArgv.AddString("0");
    8269             :         aosArgv.AddString(
    8270           1 :             CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
    8271             :     }
    8272             : 
    8273             :     // Create a warped VRT dataset
    8274             :     GDALWarpAppOptions *psOptions =
    8275          24 :         GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
    8276          24 :     GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
    8277             :     std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
    8278          48 :         GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
    8279          24 :     GDALWarpAppOptionsFree(psOptions);
    8280          24 :     if (poReprojectedDS == nullptr)
    8281           3 :         return nullptr;
    8282             : 
    8283             :     int nBlockXSize;
    8284             :     int nBlockYSize;
    8285          21 :     poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    8286          21 :     anBlockSize.emplace_back(nBlockYSize);
    8287          21 :     anBlockSize.emplace_back(nBlockXSize);
    8288             : 
    8289          21 :     double adfGeoTransform[6] = {0, 0, 0, 0, 0, 0};
    8290          21 :     CPLErr eErr = poReprojectedDS->GetGeoTransform(adfGeoTransform);
    8291          21 :     CPLAssert(eErr == CE_None);
    8292          21 :     CPL_IGNORE_RET_VAL(eErr);
    8293             : 
    8294             :     auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
    8295           0 :         std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
    8296          42 :         poReprojectedDS->GetRasterYSize());
    8297             :     auto varY = GDALMDArrayRegularlySpaced::Create(
    8298          63 :         std::string(), poDimY->GetName(), poDimY,
    8299          84 :         adfGeoTransform[3] + adfGeoTransform[5] / 2, adfGeoTransform[5], 0);
    8300          21 :     poDimY->SetIndexingVariable(varY);
    8301             : 
    8302             :     auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
    8303           0 :         std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
    8304          42 :         poReprojectedDS->GetRasterXSize());
    8305             :     auto varX = GDALMDArrayRegularlySpaced::Create(
    8306          63 :         std::string(), poDimX->GetName(), poDimX,
    8307          84 :         adfGeoTransform[0] + adfGeoTransform[1] / 2, adfGeoTransform[1], 0);
    8308          21 :     poDimX->SetIndexingVariable(varX);
    8309             : 
    8310          21 :     apoNewDims.emplace_back(poDimY);
    8311          21 :     apoNewDims.emplace_back(poDimX);
    8312             :     auto newAr(std::shared_ptr<GDALMDArrayResampled>(
    8313          42 :         new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
    8314          21 :     newAr->SetSelf(newAr);
    8315          21 :     if (poTargetSRS)
    8316             :     {
    8317           2 :         newAr->m_poSRS.reset(poTargetSRS->Clone());
    8318             :     }
    8319             :     else
    8320             :     {
    8321          19 :         newAr->m_poSRS = poParent->GetSpatialRef();
    8322             :     }
    8323          21 :     newAr->m_poVarX = varX;
    8324          21 :     newAr->m_poVarY = varY;
    8325          21 :     newAr->m_poReprojectedDS = std::move(poReprojectedDS);
    8326          21 :     newAr->m_poParentDS = std::move(poParentDS);
    8327             : 
    8328             :     // If the input array is y,x,band ordered, the above newAr is
    8329             :     // actually band,y,x ordered as it is more convenient for
    8330             :     // GDALMDArrayResampled::IRead() implementation. But transpose that
    8331             :     // array to the order of the input array
    8332          21 :     if (bYXBandOrder)
    8333           4 :         return newAr->Transpose(std::vector<int>{1, 2, 0});
    8334             : 
    8335          19 :     return newAr;
    8336             : }
    8337             : 
    8338             : /************************************************************************/
    8339             : /*                   GDALMDArrayResampled::IRead()                      */
    8340             : /************************************************************************/
    8341             : 
    8342          29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
    8343             :                                  const size_t *count, const GInt64 *arrayStep,
    8344             :                                  const GPtrDiff_t *bufferStride,
    8345             :                                  const GDALExtendedDataType &bufferDataType,
    8346             :                                  void *pDstBuffer) const
    8347             : {
    8348          29 :     if (bufferDataType.GetClass() != GEDTC_NUMERIC)
    8349           0 :         return false;
    8350             : 
    8351             :     struct Stack
    8352             :     {
    8353             :         size_t nIters = 0;
    8354             :         GByte *dst_ptr = nullptr;
    8355             :         GPtrDiff_t dst_inc_offset = 0;
    8356             :     };
    8357             : 
    8358          29 :     const auto nDims = GetDimensionCount();
    8359          58 :     std::vector<Stack> stack(nDims + 1);  // +1 to avoid -Wnull-dereference
    8360          29 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    8361          92 :     for (size_t i = 0; i < nDims; i++)
    8362             :     {
    8363          63 :         stack[i].dst_inc_offset =
    8364          63 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    8365             :     }
    8366          29 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    8367             : 
    8368          29 :     size_t dimIdx = 0;
    8369          29 :     const size_t iDimY = nDims - 2;
    8370          29 :     const size_t iDimX = nDims - 1;
    8371             :     // Use an array to avoid a false positive warning from CLang Static
    8372             :     // Analyzer about flushCaches being never read
    8373          29 :     bool flushCaches[] = {false};
    8374             :     const bool bYXBandOrder =
    8375          29 :         m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
    8376             : 
    8377          38 : lbl_next_depth:
    8378          38 :     if (dimIdx == iDimY)
    8379             :     {
    8380          33 :         if (flushCaches[0])
    8381             :         {
    8382           5 :             flushCaches[0] = false;
    8383             :             // When changing of 2D slice, flush GDAL 2D buffers
    8384           5 :             m_poParentDS->FlushCache(false);
    8385           5 :             m_poReprojectedDS->FlushCache(false);
    8386             :         }
    8387             : 
    8388          33 :         if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
    8389             :                                     GF_Read, iDimX, iDimY, arrayStartIdx, count,
    8390             :                                     arrayStep, bufferStride, bufferDataType,
    8391          33 :                                     stack[dimIdx].dst_ptr))
    8392             :         {
    8393           0 :             return false;
    8394             :         }
    8395             :     }
    8396             :     else
    8397             :     {
    8398           5 :         stack[dimIdx].nIters = count[dimIdx];
    8399           5 :         if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
    8400           5 :             arrayStartIdx[dimIdx])
    8401             :         {
    8402           1 :             flushCaches[0] = true;
    8403             :         }
    8404           5 :         m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
    8405           5 :             arrayStartIdx[dimIdx];
    8406             :         while (true)
    8407             :         {
    8408           9 :             dimIdx++;
    8409           9 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    8410           9 :             goto lbl_next_depth;
    8411           9 :         lbl_return_to_caller:
    8412           9 :             dimIdx--;
    8413           9 :             if ((--stack[dimIdx].nIters) == 0)
    8414           5 :                 break;
    8415           4 :             flushCaches[0] = true;
    8416           4 :             ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
    8417           4 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    8418             :         }
    8419             :     }
    8420          38 :     if (dimIdx > 0)
    8421           9 :         goto lbl_return_to_caller;
    8422             : 
    8423          29 :     return true;
    8424             : }
    8425             : 
    8426             : /************************************************************************/
    8427             : /*                           GetResampled()                             */
    8428             : /************************************************************************/
    8429             : 
    8430             : /** Return an array that is a resampled / reprojected view of the current array
    8431             :  *
    8432             :  * This is the same as the C function GDALMDArrayGetResampled().
    8433             :  *
    8434             :  * Currently this method can only resample along the last 2 dimensions, unless
    8435             :  * orthorectifying a NASA EMIT dataset.
    8436             :  *
    8437             :  * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
    8438             :  * geometry lookup table (GLT) is used by default for fast orthorectification.
    8439             :  *
    8440             :  * Options available are:
    8441             :  * <ul>
    8442             :  * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
    8443             :  * Can be set to NO to use generic reprojection method.
    8444             :  * </li>
    8445             :  * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
    8446             :  * orthorectification to take into account the value of the
    8447             :  * /sensor_band_parameters/good_wavelengths array to decide if slices of the
    8448             :  * current array along the band dimension are valid.</li>
    8449             :  * </ul>
    8450             :  *
    8451             :  * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
    8452             :  *                   apoNewDims[i] can be NULL to let the method automatically
    8453             :  *                   determine it.
    8454             :  * @param resampleAlg Resampling algorithm
    8455             :  * @param poTargetSRS Target SRS, or nullptr
    8456             :  * @param papszOptions NULL-terminated list of options, or NULL.
    8457             :  *
    8458             :  * @return a new array, that holds a reference to the original one, and thus is
    8459             :  * a view of it (not a copy), or nullptr in case of error.
    8460             :  *
    8461             :  * @since 3.4
    8462             :  */
    8463          38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
    8464             :     const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
    8465             :     GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
    8466             :     CSLConstList papszOptions) const
    8467             : {
    8468          76 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    8469          38 :     if (!self)
    8470             :     {
    8471           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8472             :                  "Driver implementation issue: m_pSelf not set !");
    8473           0 :         return nullptr;
    8474             :     }
    8475          38 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    8476             :     {
    8477           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8478             :                  "GetResampled() only supports numeric data type");
    8479           0 :         return nullptr;
    8480             :     }
    8481             : 
    8482             :     // Special case for NASA EMIT datasets
    8483          76 :     auto apoDims = GetDimensions();
    8484          36 :     if (poTargetSRS == nullptr &&
    8485          59 :         ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
    8486          20 :           apoDims[1]->GetName() == "crosstrack" &&
    8487          10 :           apoDims[2]->GetName() == "bands" &&
    8488          48 :           (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
    8489           1 :            apoNewDims ==
    8490          42 :                std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
    8491          30 :                                                            apoDims[2]})) ||
    8492          51 :          (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
    8493           3 :           apoDims[1]->GetName() == "crosstrack" &&
    8494          77 :           apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
    8495          13 :         CPLTestBool(CSLFetchNameValueDef(papszOptions,
    8496             :                                          "EMIT_ORTHORECTIFICATION", "YES")))
    8497             :     {
    8498           9 :         auto poRootGroup = GetRootGroup();
    8499           9 :         if (poRootGroup)
    8500             :         {
    8501          18 :             auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
    8502          18 :             auto poLocationGroup = poRootGroup->OpenGroup("location");
    8503           9 :             if (poAttrGeotransform &&
    8504           9 :                 poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
    8505           9 :                 poAttrGeotransform->GetDimensionCount() == 1 &&
    8506          27 :                 poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
    8507           9 :                 poLocationGroup)
    8508             :             {
    8509          18 :                 auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
    8510          18 :                 auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
    8511          27 :                 if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
    8512          18 :                     poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
    8513          18 :                     poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
    8514          27 :                     poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
    8515          27 :                     poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
    8516           9 :                     poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
    8517             :                 {
    8518             :                     return CreateGLTOrthorectified(
    8519             :                         self, poRootGroup, poGLT_X, poGLT_Y,
    8520             :                         /* nGLTIndexOffset = */ -1,
    8521          18 :                         poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
    8522             :                 }
    8523             :             }
    8524             :         }
    8525             :     }
    8526             : 
    8527          29 :     if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
    8528             :                                          "EMIT_ORTHORECTIFICATION", "NO")))
    8529             :     {
    8530           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8531             :                  "EMIT_ORTHORECTIFICATION required, but dataset and/or "
    8532             :                  "parameters are not compatible with it");
    8533           0 :         return nullptr;
    8534             :     }
    8535             : 
    8536             :     return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
    8537          29 :                                         poTargetSRS, papszOptions);
    8538             : }
    8539             : 
    8540             : /************************************************************************/
    8541             : /*                         GDALDatasetFromArray()                       */
    8542             : /************************************************************************/
    8543             : 
    8544             : class GDALDatasetFromArray;
    8545             : 
    8546             : namespace
    8547             : {
    8548             : struct MetadataItem
    8549             : {
    8550             :     std::shared_ptr<GDALMDArray> poArray{};
    8551             :     std::string osName{};
    8552             :     std::string osDefinition{};
    8553             :     bool bDefinitionUsesPctForG = false;
    8554             : };
    8555             : }  // namespace
    8556             : 
    8557             : class GDALRasterBandFromArray final : public GDALPamRasterBand
    8558             : {
    8559             :     std::vector<GUInt64> m_anOffset{};
    8560             :     std::vector<size_t> m_anCount{};
    8561             :     std::vector<GPtrDiff_t> m_anStride{};
    8562             : 
    8563             :   protected:
    8564             :     CPLErr IReadBlock(int, int, void *) override;
    8565             :     CPLErr IWriteBlock(int, int, void *) override;
    8566             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
    8567             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
    8568             :                      GDALDataType eBufType, GSpacing nPixelSpaceBuf,
    8569             :                      GSpacing nLineSpaceBuf,
    8570             :                      GDALRasterIOExtraArg *psExtraArg) override;
    8571             : 
    8572             :   public:
    8573             :     explicit GDALRasterBandFromArray(
    8574             :         GDALDatasetFromArray *poDSIn,
    8575             :         const std::vector<GUInt64> &anOtherDimCoord,
    8576             :         const std::vector<std::vector<MetadataItem>>
    8577             :             &aoBandParameterMetadataItems,
    8578             :         double dfDelay, time_t nStartTime, bool &bHasWarned);
    8579             : 
    8580             :     double GetNoDataValue(int *pbHasNoData) override;
    8581             :     int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
    8582             :     uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
    8583             :     double GetOffset(int *pbHasOffset) override;
    8584             :     double GetScale(int *pbHasScale) override;
    8585             :     const char *GetUnitType() override;
    8586             :     GDALColorInterp GetColorInterpretation() override;
    8587             : };
    8588             : 
    8589             : class GDALDatasetFromArray final : public GDALPamDataset
    8590             : {
    8591             :     friend class GDALRasterBandFromArray;
    8592             : 
    8593             :     std::shared_ptr<GDALMDArray> m_poArray;
    8594             :     size_t m_iXDim;
    8595             :     size_t m_iYDim;
    8596             :     double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
    8597             :     bool m_bHasGT = false;
    8598             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
    8599             :     GDALMultiDomainMetadata m_oMDD{};
    8600             :     std::string m_osOvrFilename{};
    8601             : 
    8602             :   public:
    8603         183 :     GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
    8604             :                          size_t iXDim, size_t iYDim)
    8605         183 :         : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
    8606             :     {
    8607             :         // Initialize an overview filename from the filename of the array
    8608             :         // and its name.
    8609         183 :         const std::string &osFilename = m_poArray->GetFilename();
    8610         183 :         if (!osFilename.empty())
    8611             :         {
    8612         165 :             m_osOvrFilename = osFilename;
    8613         165 :             m_osOvrFilename += '.';
    8614        6327 :             for (char ch : m_poArray->GetName())
    8615             :             {
    8616        6162 :                 if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
    8617        5463 :                     (ch >= 'a' && ch <= 'z') || ch == '_')
    8618             :                 {
    8619        4925 :                     m_osOvrFilename += ch;
    8620             :                 }
    8621             :                 else
    8622             :                 {
    8623        1237 :                     m_osOvrFilename += '_';
    8624             :                 }
    8625             :             }
    8626         165 :             m_osOvrFilename += ".ovr";
    8627         165 :             oOvManager.Initialize(this);
    8628             :         }
    8629         183 :     }
    8630             : 
    8631             :     static GDALDatasetFromArray *
    8632             :     Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
    8633             :            size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
    8634             :            CSLConstList papszOptions);
    8635             : 
    8636         366 :     ~GDALDatasetFromArray()
    8637         183 :     {
    8638         183 :         GDALDatasetFromArray::Close();
    8639         366 :     }
    8640             : 
    8641         299 :     CPLErr Close() override
    8642             :     {
    8643         299 :         CPLErr eErr = CE_None;
    8644         299 :         if (nOpenFlags != OPEN_FLAGS_CLOSED)
    8645             :         {
    8646         299 :             if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
    8647             :                 CE_None)
    8648           0 :                 eErr = CE_Failure;
    8649         299 :             m_poArray.reset();
    8650             :         }
    8651         299 :         return eErr;
    8652             :     }
    8653             : 
    8654          49 :     CPLErr GetGeoTransform(double *padfGeoTransform) override
    8655             :     {
    8656          49 :         memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
    8657          49 :         return m_bHasGT ? CE_None : CE_Failure;
    8658             :     }
    8659             : 
    8660          53 :     const OGRSpatialReference *GetSpatialRef() const override
    8661             :     {
    8662          53 :         if (m_poArray->GetDimensionCount() < 2)
    8663           3 :             return nullptr;
    8664          50 :         m_poSRS = m_poArray->GetSpatialRef();
    8665          50 :         if (m_poSRS)
    8666             :         {
    8667          16 :             m_poSRS.reset(m_poSRS->Clone());
    8668          32 :             auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
    8669          48 :             for (auto &m : axisMapping)
    8670             :             {
    8671          32 :                 if (m == static_cast<int>(m_iXDim) + 1)
    8672          16 :                     m = 1;
    8673          16 :                 else if (m == static_cast<int>(m_iYDim) + 1)
    8674          16 :                     m = 2;
    8675             :             }
    8676          16 :             m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
    8677             :         }
    8678          50 :         return m_poSRS.get();
    8679             :     }
    8680             : 
    8681           4 :     CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
    8682             :     {
    8683           4 :         return m_oMDD.SetMetadata(papszMetadata, pszDomain);
    8684             :     }
    8685             : 
    8686         144 :     char **GetMetadata(const char *pszDomain) override
    8687             :     {
    8688         144 :         return m_oMDD.GetMetadata(pszDomain);
    8689             :     }
    8690             : 
    8691         204 :     const char *GetMetadataItem(const char *pszName,
    8692             :                                 const char *pszDomain) override
    8693             :     {
    8694         378 :         if (!m_osOvrFilename.empty() && pszName &&
    8695         390 :             EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
    8696          12 :             EQUAL(pszDomain, "OVERVIEWS"))
    8697             :         {
    8698          12 :             return m_osOvrFilename.c_str();
    8699             :         }
    8700         192 :         return m_oMDD.GetMetadataItem(pszName, pszDomain);
    8701             :     }
    8702             : };
    8703             : 
    8704             : /************************************************************************/
    8705             : /*                      GDALRasterBandFromArray()                       */
    8706             : /************************************************************************/
    8707             : 
    8708         243 : GDALRasterBandFromArray::GDALRasterBandFromArray(
    8709             :     GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
    8710             :     const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
    8711         243 :     double dfDelay, time_t nStartTime, bool &bHasWarned)
    8712             : {
    8713         243 :     const auto &poArray(poDSIn->m_poArray);
    8714         243 :     const auto &dims(poArray->GetDimensions());
    8715         243 :     const auto nDimCount(dims.size());
    8716         486 :     const auto blockSize(poArray->GetBlockSize());
    8717         232 :     nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
    8718         475 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    8719         128 :                                                   blockSize[poDSIn->m_iYDim]))
    8720             :                       : 1;
    8721         243 :     nBlockXSize = blockSize[poDSIn->m_iXDim]
    8722         139 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    8723         139 :                                                   blockSize[poDSIn->m_iXDim]))
    8724         243 :                       : poDSIn->GetRasterXSize();
    8725         243 :     eDataType = poArray->GetDataType().GetNumericDataType();
    8726         243 :     eAccess = poDSIn->eAccess;
    8727         243 :     m_anOffset.resize(nDimCount);
    8728         243 :     m_anCount.resize(nDimCount, 1);
    8729         243 :     m_anStride.resize(nDimCount);
    8730         822 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    8731             :     {
    8732         579 :         if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
    8733             :         {
    8734         208 :             std::string dimName(dims[i]->GetName());
    8735         104 :             GUInt64 nIndex = anOtherDimCoord[j];
    8736             :             // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
    8737             :             // subsetted dimensions as generated by GetView()
    8738         104 :             if (STARTS_WITH(dimName.c_str(), "subset_"))
    8739             :             {
    8740             :                 CPLStringList aosTokens(
    8741          12 :                     CSLTokenizeString2(dimName.c_str(), "_", 0));
    8742           6 :                 if (aosTokens.size() == 5)
    8743             :                 {
    8744           6 :                     dimName = aosTokens[1];
    8745          18 :                     const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
    8746           6 :                         aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
    8747           6 :                     const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
    8748           6 :                     nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
    8749           0 :                                           : nStartDim - (nIndex * -nIncrDim);
    8750             :                 }
    8751             :             }
    8752         104 :             if (nDimCount != 3 || dimName != "Band")
    8753             :             {
    8754          48 :                 SetMetadataItem(
    8755             :                     CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
    8756             :                     CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
    8757             :             }
    8758             : 
    8759         104 :             auto indexingVar = dims[i]->GetIndexingVariable();
    8760             : 
    8761             :             // If the indexing variable is also listed in band parameter arrays,
    8762             :             // then don't use our default formatting
    8763         104 :             if (indexingVar)
    8764             :             {
    8765          38 :                 for (const auto &oItem : aoBandParameterMetadataItems[j])
    8766             :                 {
    8767          12 :                     if (oItem.poArray->GetFullName() ==
    8768          12 :                         indexingVar->GetFullName())
    8769             :                     {
    8770          12 :                         indexingVar.reset();
    8771          12 :                         break;
    8772             :                     }
    8773             :                 }
    8774             :             }
    8775             : 
    8776         130 :             if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
    8777          26 :                 indexingVar->GetDimensions()[0]->GetSize() ==
    8778          26 :                     dims[i]->GetSize())
    8779             :             {
    8780          26 :                 if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
    8781             :                 {
    8782           0 :                     if (!bHasWarned)
    8783             :                     {
    8784           0 :                         CPLError(
    8785             :                             CE_Warning, CPLE_AppDefined,
    8786             :                             "Maximum delay to load band metadata from "
    8787             :                             "dimension indexing variables has expired. "
    8788             :                             "Increase the value of the "
    8789             :                             "LOAD_EXTRA_DIM_METADATA_DELAY "
    8790             :                             "option of GDALMDArray::AsClassicDataset() "
    8791             :                             "(also accessible as the "
    8792             :                             "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
    8793             :                             "configuration option), "
    8794             :                             "or set it to 'unlimited' for unlimited delay. ");
    8795           0 :                         bHasWarned = true;
    8796             :                     }
    8797             :                 }
    8798             :                 else
    8799             :                 {
    8800          26 :                     size_t nCount = 1;
    8801          26 :                     const auto &dt(indexingVar->GetDataType());
    8802          52 :                     std::vector<GByte> abyTmp(dt.GetSize());
    8803          52 :                     if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
    8804          26 :                                           nullptr, nullptr, dt, &abyTmp[0]))
    8805             :                     {
    8806          26 :                         char *pszTmp = nullptr;
    8807          26 :                         GDALExtendedDataType::CopyValue(
    8808          26 :                             &abyTmp[0], dt, &pszTmp,
    8809          52 :                             GDALExtendedDataType::CreateString());
    8810          26 :                         if (pszTmp)
    8811             :                         {
    8812          26 :                             SetMetadataItem(
    8813             :                                 CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
    8814             :                                 pszTmp);
    8815          26 :                             CPLFree(pszTmp);
    8816             :                         }
    8817             : 
    8818          26 :                         const auto &unit(indexingVar->GetUnit());
    8819          26 :                         if (!unit.empty())
    8820             :                         {
    8821          12 :                             SetMetadataItem(
    8822             :                                 CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
    8823             :                                 unit.c_str());
    8824             :                         }
    8825             :                     }
    8826             :                 }
    8827             :             }
    8828             : 
    8829         120 :             for (const auto &oItem : aoBandParameterMetadataItems[j])
    8830             :             {
    8831          32 :                 CPLString osVal;
    8832             : 
    8833          16 :                 size_t nCount = 1;
    8834          16 :                 const auto &dt(oItem.poArray->GetDataType());
    8835          16 :                 if (oItem.bDefinitionUsesPctForG)
    8836             :                 {
    8837             :                     // There is one and only one %[x][.y]f|g in osDefinition
    8838          12 :                     std::vector<GByte> abyTmp(dt.GetSize());
    8839          12 :                     if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
    8840           6 :                                             nullptr, nullptr, dt, &abyTmp[0]))
    8841             :                     {
    8842           6 :                         double dfVal = 0;
    8843           6 :                         GDALExtendedDataType::CopyValue(
    8844           6 :                             &abyTmp[0], dt, &dfVal,
    8845          12 :                             GDALExtendedDataType::Create(GDT_Float64));
    8846           6 :                         osVal.Printf(oItem.osDefinition.c_str(), dfVal);
    8847             :                     }
    8848             :                 }
    8849             :                 else
    8850             :                 {
    8851             :                     // There should be zero or one %s in osDefinition
    8852          10 :                     char *pszValue = nullptr;
    8853          10 :                     if (dt.GetClass() == GEDTC_STRING)
    8854             :                     {
    8855           4 :                         CPL_IGNORE_RET_VAL(oItem.poArray->Read(
    8856           2 :                             &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
    8857             :                             dt, &pszValue));
    8858             :                     }
    8859             :                     else
    8860             :                     {
    8861          16 :                         std::vector<GByte> abyTmp(dt.GetSize());
    8862          16 :                         if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
    8863             :                                                 nullptr, nullptr, dt,
    8864           8 :                                                 &abyTmp[0]))
    8865             :                         {
    8866           8 :                             GDALExtendedDataType::CopyValue(
    8867           8 :                                 &abyTmp[0], dt, &pszValue,
    8868          16 :                                 GDALExtendedDataType::CreateString());
    8869             :                         }
    8870             :                     }
    8871             : 
    8872          10 :                     if (pszValue)
    8873             :                     {
    8874          10 :                         osVal.Printf(oItem.osDefinition.c_str(), pszValue);
    8875          10 :                         CPLFree(pszValue);
    8876             :                     }
    8877             :                 }
    8878          16 :                 if (!osVal.empty())
    8879          16 :                     SetMetadataItem(oItem.osName.c_str(), osVal);
    8880             :             }
    8881             : 
    8882         104 :             m_anOffset[i] = anOtherDimCoord[j];
    8883         104 :             j++;
    8884             :         }
    8885             :     }
    8886         243 : }
    8887             : 
    8888             : /************************************************************************/
    8889             : /*                           GetNoDataValue()                           */
    8890             : /************************************************************************/
    8891             : 
    8892          93 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
    8893             : {
    8894          93 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8895          93 :     const auto &poArray(l_poDS->m_poArray);
    8896          93 :     bool bHasNodata = false;
    8897          93 :     const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
    8898          93 :     if (pbHasNoData)
    8899          81 :         *pbHasNoData = bHasNodata;
    8900          93 :     return res;
    8901             : }
    8902             : 
    8903             : /************************************************************************/
    8904             : /*                       GetNoDataValueAsInt64()                        */
    8905             : /************************************************************************/
    8906             : 
    8907           1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
    8908             : {
    8909           1 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8910           1 :     const auto &poArray(l_poDS->m_poArray);
    8911           1 :     bool bHasNodata = false;
    8912           1 :     const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
    8913           1 :     if (pbHasNoData)
    8914           1 :         *pbHasNoData = bHasNodata;
    8915           1 :     return nodata;
    8916             : }
    8917             : 
    8918             : /************************************************************************/
    8919             : /*                      GetNoDataValueAsUInt64()                        */
    8920             : /************************************************************************/
    8921             : 
    8922           1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
    8923             : {
    8924           1 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8925           1 :     const auto &poArray(l_poDS->m_poArray);
    8926           1 :     bool bHasNodata = false;
    8927           1 :     const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
    8928           1 :     if (pbHasNoData)
    8929           1 :         *pbHasNoData = bHasNodata;
    8930           1 :     return nodata;
    8931             : }
    8932             : 
    8933             : /************************************************************************/
    8934             : /*                             GetOffset()                              */
    8935             : /************************************************************************/
    8936             : 
    8937          29 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
    8938             : {
    8939          29 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8940          29 :     const auto &poArray(l_poDS->m_poArray);
    8941          29 :     bool bHasValue = false;
    8942          29 :     double dfRes = poArray->GetOffset(&bHasValue);
    8943          29 :     if (pbHasOffset)
    8944          17 :         *pbHasOffset = bHasValue;
    8945          29 :     return dfRes;
    8946             : }
    8947             : 
    8948             : /************************************************************************/
    8949             : /*                           GetUnitType()                              */
    8950             : /************************************************************************/
    8951             : 
    8952          36 : const char *GDALRasterBandFromArray::GetUnitType()
    8953             : {
    8954          36 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8955          36 :     const auto &poArray(l_poDS->m_poArray);
    8956          36 :     return poArray->GetUnit().c_str();
    8957             : }
    8958             : 
    8959             : /************************************************************************/
    8960             : /*                             GetScale()                              */
    8961             : /************************************************************************/
    8962             : 
    8963          27 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
    8964             : {
    8965          27 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8966          27 :     const auto &poArray(l_poDS->m_poArray);
    8967          27 :     bool bHasValue = false;
    8968          27 :     double dfRes = poArray->GetScale(&bHasValue);
    8969          27 :     if (pbHasScale)
    8970          15 :         *pbHasScale = bHasValue;
    8971          27 :     return dfRes;
    8972             : }
    8973             : 
    8974             : /************************************************************************/
    8975             : /*                            IReadBlock()                              */
    8976             : /************************************************************************/
    8977             : 
    8978          68 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
    8979             :                                            void *pImage)
    8980             : {
    8981          68 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    8982          68 :     const int nXOff = nBlockXOff * nBlockXSize;
    8983          68 :     const int nYOff = nBlockYOff * nBlockYSize;
    8984          68 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    8985          68 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    8986             :     GDALRasterIOExtraArg sExtraArg;
    8987          68 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    8988         136 :     return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    8989             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    8990         136 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    8991             : }
    8992             : 
    8993             : /************************************************************************/
    8994             : /*                            IWriteBlock()                             */
    8995             : /************************************************************************/
    8996             : 
    8997           0 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
    8998             :                                             void *pImage)
    8999             : {
    9000           0 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    9001           0 :     const int nXOff = nBlockXOff * nBlockXSize;
    9002           0 :     const int nYOff = nBlockYOff * nBlockYSize;
    9003           0 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    9004           0 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    9005             :     GDALRasterIOExtraArg sExtraArg;
    9006           0 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    9007           0 :     return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    9008             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    9009           0 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    9010             : }
    9011             : 
    9012             : /************************************************************************/
    9013             : /*                            IRasterIO()                               */
    9014             : /************************************************************************/
    9015             : 
    9016         319 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
    9017             :                                           int nYOff, int nXSize, int nYSize,
    9018             :                                           void *pData, int nBufXSize,
    9019             :                                           int nBufYSize, GDALDataType eBufType,
    9020             :                                           GSpacing nPixelSpaceBuf,
    9021             :                                           GSpacing nLineSpaceBuf,
    9022             :                                           GDALRasterIOExtraArg *psExtraArg)
    9023             : {
    9024         319 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9025         319 :     const auto &poArray(l_poDS->m_poArray);
    9026         319 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
    9027         319 :     if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
    9028         319 :         (nPixelSpaceBuf % nBufferDTSize) == 0 &&
    9029         319 :         (nLineSpaceBuf % nBufferDTSize) == 0)
    9030             :     {
    9031         319 :         m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
    9032         319 :         m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
    9033         638 :         m_anStride[l_poDS->m_iXDim] =
    9034         319 :             static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
    9035         319 :         if (poArray->GetDimensionCount() >= 2)
    9036             :         {
    9037         310 :             m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
    9038         310 :             m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
    9039         310 :             m_anStride[l_poDS->m_iYDim] =
    9040         310 :                 static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
    9041             :         }
    9042         319 :         if (eRWFlag == GF_Read)
    9043             :         {
    9044         630 :             return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
    9045         315 :                                  m_anStride.data(),
    9046         630 :                                  GDALExtendedDataType::Create(eBufType), pData)
    9047         315 :                        ? CE_None
    9048         315 :                        : CE_Failure;
    9049             :         }
    9050             :         else
    9051             :         {
    9052           8 :             return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
    9053           4 :                                   m_anStride.data(),
    9054           8 :                                   GDALExtendedDataType::Create(eBufType), pData)
    9055           4 :                        ? CE_None
    9056           4 :                        : CE_Failure;
    9057             :         }
    9058             :     }
    9059           0 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    9060             :                                      pData, nBufXSize, nBufYSize, eBufType,
    9061           0 :                                      nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
    9062             : }
    9063             : 
    9064             : /************************************************************************/
    9065             : /*                      GetColorInterpretation()                        */
    9066             : /************************************************************************/
    9067             : 
    9068          45 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
    9069             : {
    9070          45 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9071          45 :     const auto &poArray(l_poDS->m_poArray);
    9072         135 :     auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
    9073          45 :     if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
    9074             :     {
    9075           6 :         bool bOK = false;
    9076           6 :         GUInt64 nStartIndex = 0;
    9077           6 :         if (poArray->GetDimensionCount() == 2 &&
    9078           0 :             poAttr->GetDimensionCount() == 0)
    9079             :         {
    9080           0 :             bOK = true;
    9081             :         }
    9082           6 :         else if (poArray->GetDimensionCount() == 3)
    9083             :         {
    9084           6 :             uint64_t nExtraDimSamples = 1;
    9085           6 :             const auto &apoDims = poArray->GetDimensions();
    9086          24 :             for (size_t i = 0; i < apoDims.size(); ++i)
    9087             :             {
    9088          18 :                 if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
    9089           6 :                     nExtraDimSamples *= apoDims[i]->GetSize();
    9090             :             }
    9091           6 :             if (poAttr->GetDimensionsSize() ==
    9092          12 :                 std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
    9093             :             {
    9094           6 :                 bOK = true;
    9095             :             }
    9096           6 :             nStartIndex = nBand - 1;
    9097             :         }
    9098           6 :         if (bOK)
    9099             :         {
    9100           6 :             const auto oStringDT = GDALExtendedDataType::CreateString();
    9101           6 :             const size_t nCount = 1;
    9102           6 :             const GInt64 arrayStep = 1;
    9103           6 :             const GPtrDiff_t bufferStride = 1;
    9104           6 :             char *pszValue = nullptr;
    9105           6 :             poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
    9106           6 :                          oStringDT, &pszValue);
    9107           6 :             if (pszValue)
    9108             :             {
    9109             :                 const auto eColorInterp =
    9110           6 :                     GDALGetColorInterpretationByName(pszValue);
    9111           6 :                 CPLFree(pszValue);
    9112           6 :                 return eColorInterp;
    9113             :             }
    9114             :         }
    9115             :     }
    9116          39 :     return GCI_Undefined;
    9117             : }
    9118             : 
    9119             : /************************************************************************/
    9120             : /*                    GDALDatasetFromArray::Create()                    */
    9121             : /************************************************************************/
    9122             : 
    9123         206 : GDALDatasetFromArray *GDALDatasetFromArray::Create(
    9124             :     const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
    9125             :     const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
    9126             : 
    9127             : {
    9128         206 :     const auto nDimCount(array->GetDimensionCount());
    9129         206 :     if (nDimCount == 0)
    9130             :     {
    9131           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    9132             :                  "Unsupported number of dimensions");
    9133           1 :         return nullptr;
    9134             :     }
    9135         409 :     if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
    9136         204 :         array->GetDataType().GetNumericDataType() == GDT_Unknown)
    9137             :     {
    9138           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    9139             :                  "Only arrays with numeric data types "
    9140             :                  "can be exposed as classic GDALDataset");
    9141           1 :         return nullptr;
    9142             :     }
    9143         204 :     if (iXDim >= nDimCount ||
    9144         190 :         (nDimCount >= 2 && (iYDim >= nDimCount || iXDim == iYDim)))
    9145             :     {
    9146           6 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
    9147           6 :         return nullptr;
    9148             :     }
    9149         198 :     GUInt64 nTotalBands = 1;
    9150         198 :     const auto &dims(array->GetDimensions());
    9151         634 :     for (size_t i = 0; i < nDimCount; ++i)
    9152             :     {
    9153         437 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9154             :         {
    9155          54 :             if (dims[i]->GetSize() > 65536 / nTotalBands)
    9156             :             {
    9157           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9158             :                          "Too many bands. Operate on a sliced view");
    9159           1 :                 return nullptr;
    9160             :             }
    9161          53 :             nTotalBands *= dims[i]->GetSize();
    9162             :         }
    9163             :     }
    9164             : 
    9165             :     const char *pszBandMetadata =
    9166         197 :         CSLFetchNameValue(papszOptions, "BAND_METADATA");
    9167         197 :     const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
    9168             :     std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
    9169         394 :         nNewDimCount);
    9170             : 
    9171         197 :     if (pszBandMetadata)
    9172             :     {
    9173          21 :         if (!poRootGroup)
    9174             :         {
    9175           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9176             :                      "Root group should be provided when BAND_METADATA is set");
    9177          14 :             return nullptr;
    9178             :         }
    9179          20 :         CPLJSONDocument oDoc;
    9180          20 :         if (!oDoc.LoadMemory(pszBandMetadata))
    9181             :         {
    9182           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9183             :                      "Invalid JSON content for BAND_METADATA");
    9184           1 :             return nullptr;
    9185             :         }
    9186          19 :         auto oRoot = oDoc.GetRoot();
    9187          19 :         if (oRoot.GetType() != CPLJSONObject::Type::Array)
    9188             :         {
    9189           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9190             :                      "Value of BAND_METADATA should be an array");
    9191           1 :             return nullptr;
    9192             :         }
    9193             : 
    9194          18 :         std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
    9195          72 :         for (size_t i = 0, j = 0; i < nDimCount; ++i)
    9196             :         {
    9197          54 :             if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9198             :             {
    9199          18 :                 oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
    9200          18 :                 ++j;
    9201             :             }
    9202             :         }
    9203             : 
    9204          18 :         auto oArray = oRoot.ToArray();
    9205          26 :         for (int j = 0; j < oArray.Size(); ++j)
    9206             :         {
    9207          19 :             const auto oJsonItem = oArray[j];
    9208          19 :             MetadataItem oItem;
    9209             : 
    9210          38 :             auto osBandArrayFullname = oJsonItem.GetString("array");
    9211          19 :             if (osBandArrayFullname.empty())
    9212             :             {
    9213           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9214             :                          "BAND_METADATA[%d][\"array\"] is missing", j);
    9215           1 :                 return nullptr;
    9216             :             }
    9217             :             oItem.poArray =
    9218          18 :                 poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
    9219          18 :             if (!oItem.poArray)
    9220             :             {
    9221           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9222             :                          "Array %s cannot be found",
    9223             :                          osBandArrayFullname.c_str());
    9224           1 :                 return nullptr;
    9225             :             }
    9226          17 :             if (oItem.poArray->GetDimensionCount() != 1)
    9227             :             {
    9228           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9229             :                          "Array %s is not a 1D array",
    9230             :                          osBandArrayFullname.c_str());
    9231           1 :                 return nullptr;
    9232             :             }
    9233             :             const auto &osAuxArrayDimName =
    9234          16 :                 oItem.poArray->GetDimensions()[0]->GetName();
    9235          16 :             auto oIter = oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
    9236          16 :             if (oIter == oMapArrayDimNameToExtraDimIdx.end())
    9237             :             {
    9238           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9239             :                          "Dimension %s of array %s is not a non-X/Y dimension "
    9240             :                          "of array %s",
    9241             :                          osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
    9242           1 :                          array->GetName().c_str());
    9243           1 :                 return nullptr;
    9244             :             }
    9245          15 :             const size_t iExtraDimIdx = oIter->second;
    9246          15 :             CPLAssert(iExtraDimIdx < nNewDimCount);
    9247             : 
    9248          15 :             oItem.osName = oJsonItem.GetString("item_name");
    9249          15 :             if (oItem.osName.empty())
    9250             :             {
    9251           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9252             :                          "BAND_METADATA[%d][\"item_name\"] is missing", j);
    9253           1 :                 return nullptr;
    9254             :             }
    9255             : 
    9256          28 :             const auto osDefinition = oJsonItem.GetString("item_value", "%s");
    9257             : 
    9258             :             // Check correctness of definition
    9259          14 :             bool bFirstNumericFormatter = true;
    9260          14 :             std::string osModDefinition;
    9261          14 :             bool bDefinitionUsesPctForG = false;
    9262          72 :             for (size_t k = 0; k < osDefinition.size(); ++k)
    9263             :             {
    9264          64 :                 if (osDefinition[k] == '%')
    9265             :                 {
    9266          13 :                     osModDefinition += osDefinition[k];
    9267          13 :                     if (k + 1 == osDefinition.size())
    9268             :                     {
    9269           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9270             :                                  "Value of "
    9271             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9272             :                                  "%s is invalid at offset %d",
    9273             :                                  osAuxArrayDimName.c_str(), j,
    9274             :                                  osDefinition.c_str(), int(k));
    9275           1 :                         return nullptr;
    9276             :                     }
    9277          12 :                     ++k;
    9278          12 :                     if (osDefinition[k] == '%')
    9279             :                     {
    9280           1 :                         osModDefinition += osDefinition[k];
    9281           1 :                         continue;
    9282             :                     }
    9283          11 :                     if (!bFirstNumericFormatter)
    9284             :                     {
    9285           1 :                         CPLError(
    9286             :                             CE_Failure, CPLE_AppDefined,
    9287             :                             "Value of "
    9288             :                             "BAND_METADATA[\"%s\"][%d][\"item_value\"] = %s is "
    9289             :                             "invalid at offset %d: %%[x][.y]f|g or %%s "
    9290             :                             "formatters should be specified at most once",
    9291             :                             osAuxArrayDimName.c_str(), j, osDefinition.c_str(),
    9292             :                             int(k));
    9293           1 :                         return nullptr;
    9294             :                     }
    9295          10 :                     bFirstNumericFormatter = false;
    9296          13 :                     for (; k < osDefinition.size(); ++k)
    9297             :                     {
    9298          13 :                         osModDefinition += osDefinition[k];
    9299          26 :                         if (!((osDefinition[k] >= '0' &&
    9300          12 :                                osDefinition[k] <= '9') ||
    9301          11 :                               osDefinition[k] == '.'))
    9302          10 :                             break;
    9303             :                     }
    9304          20 :                     if (k == osDefinition.size() ||
    9305          10 :                         (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
    9306           5 :                          osDefinition[k] != 's'))
    9307             :                     {
    9308           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9309             :                                  "Value of "
    9310             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9311             :                                  "%s is invalid at offset %d: only "
    9312             :                                  "%%[x][.y]f|g or %%s formatters are accepted",
    9313             :                                  osAuxArrayDimName.c_str(), j,
    9314             :                                  osDefinition.c_str(), int(k));
    9315           1 :                         return nullptr;
    9316             :                     }
    9317           9 :                     bDefinitionUsesPctForG =
    9318           9 :                         (osDefinition[k] == 'f' || osDefinition[k] == 'g');
    9319           9 :                     if (bDefinitionUsesPctForG)
    9320             :                     {
    9321           5 :                         if (oItem.poArray->GetDataType().GetClass() !=
    9322             :                             GEDTC_NUMERIC)
    9323             :                         {
    9324           1 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9325             :                                      "Data type of %s array is not numeric",
    9326             :                                      osAuxArrayDimName.c_str());
    9327           1 :                             return nullptr;
    9328             :                         }
    9329             :                     }
    9330             :                 }
    9331          56 :                 else if (osDefinition[k] == '$' &&
    9332          56 :                          k + 1 < osDefinition.size() &&
    9333           5 :                          osDefinition[k + 1] == '{')
    9334             :                 {
    9335           5 :                     const auto nPos = osDefinition.find('}', k);
    9336           5 :                     if (nPos == std::string::npos)
    9337             :                     {
    9338           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9339             :                                  "Value of "
    9340             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9341             :                                  "%s is invalid at offset %d",
    9342             :                                  osAuxArrayDimName.c_str(), j,
    9343             :                                  osDefinition.c_str(), int(k));
    9344           2 :                         return nullptr;
    9345             :                     }
    9346             :                     const auto osAttrName =
    9347           4 :                         osDefinition.substr(k + 2, nPos - (k + 2));
    9348           4 :                     auto poAttr = oItem.poArray->GetAttribute(osAttrName);
    9349           4 :                     if (!poAttr)
    9350             :                     {
    9351           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9352             :                                  "Value of "
    9353             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9354             :                                  "%s is invalid: %s is not an attribute of %s",
    9355             :                                  osAuxArrayDimName.c_str(), j,
    9356             :                                  osDefinition.c_str(), osAttrName.c_str(),
    9357             :                                  osAuxArrayDimName.c_str());
    9358           1 :                         return nullptr;
    9359             :                     }
    9360           3 :                     k = nPos;
    9361           3 :                     const char *pszValue = poAttr->ReadAsString();
    9362           3 :                     if (!pszValue)
    9363             :                     {
    9364           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9365             :                                  "Cannot get value of attribute %s of %s as a "
    9366             :                                  "string",
    9367             :                                  osAttrName.c_str(), osAuxArrayDimName.c_str());
    9368           0 :                         return nullptr;
    9369             :                     }
    9370           3 :                     osModDefinition += pszValue;
    9371             :                 }
    9372             :                 else
    9373             :                 {
    9374          46 :                     osModDefinition += osDefinition[k];
    9375             :                 }
    9376             :             }
    9377             : 
    9378           8 :             oItem.osDefinition = std::move(osModDefinition);
    9379           8 :             oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
    9380             : 
    9381           8 :             aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
    9382           8 :                 std::move(oItem));
    9383             :         }
    9384             :     }
    9385             : 
    9386         366 :     auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
    9387             : 
    9388         183 :     poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
    9389             : 
    9390         183 :     poDS->nRasterYSize =
    9391         183 :         nDimCount < 2 ? 1
    9392         172 :                       : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    9393         172 :                                                   dims[iYDim]->GetSize()));
    9394         366 :     poDS->nRasterXSize = static_cast<int>(
    9395         183 :         std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
    9396             : 
    9397         366 :     std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
    9398         366 :     std::vector<GUInt64> anStackIters(nDimCount);
    9399         366 :     std::vector<size_t> anMapNewToOld(nNewDimCount);
    9400         577 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    9401             :     {
    9402         394 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9403             :         {
    9404          39 :             anMapNewToOld[j] = i;
    9405          39 :             j++;
    9406             :         }
    9407             :     }
    9408             : 
    9409         366 :     poDS->m_bHasGT =
    9410         183 :         array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_adfGeoTransform);
    9411             : 
    9412         366 :     const auto attrs(array->GetAttributes());
    9413         261 :     for (const auto &attr : attrs)
    9414             :     {
    9415          78 :         if (attr->GetName() != "COLOR_INTERPRETATION")
    9416             :         {
    9417         144 :             auto stringArray = attr->ReadAsStringArray();
    9418         144 :             std::string val;
    9419          72 :             if (stringArray.size() > 1)
    9420             :             {
    9421          22 :                 val += '{';
    9422             :             }
    9423         166 :             for (int i = 0; i < stringArray.size(); ++i)
    9424             :             {
    9425          94 :                 if (i > 0)
    9426          22 :                     val += ',';
    9427          94 :                 val += stringArray[i];
    9428             :             }
    9429          72 :             if (stringArray.size() > 1)
    9430             :             {
    9431          22 :                 val += '}';
    9432             :             }
    9433          72 :             poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
    9434             :         }
    9435             :     }
    9436             : 
    9437         183 :     const char *pszDelay = CSLFetchNameValueDef(
    9438             :         papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
    9439             :         CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
    9440             :     const double dfDelay =
    9441         183 :         EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
    9442         183 :     const auto nStartTime = time(nullptr);
    9443         183 :     bool bHasWarned = false;
    9444             :     // Instantiate bands by iterating over non-XY variables
    9445         183 :     size_t iDim = 0;
    9446         183 :     int nCurBand = 1;
    9447         284 : lbl_next_depth:
    9448         284 :     if (iDim < nNewDimCount)
    9449             :     {
    9450          41 :         anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
    9451          41 :         anOtherDimCoord[iDim] = 0;
    9452             :         while (true)
    9453             :         {
    9454         101 :             ++iDim;
    9455         101 :             goto lbl_next_depth;
    9456         101 :         lbl_return_to_caller:
    9457         101 :             --iDim;
    9458         101 :             --anStackIters[iDim];
    9459         101 :             if (anStackIters[iDim] == 0)
    9460          41 :                 break;
    9461          60 :             ++anOtherDimCoord[iDim];
    9462             :         }
    9463             :     }
    9464             :     else
    9465             :     {
    9466         486 :         poDS->SetBand(nCurBand, new GDALRasterBandFromArray(
    9467         243 :                                     poDS.get(), anOtherDimCoord,
    9468             :                                     aoBandParameterMetadataItems, dfDelay,
    9469         243 :                                     nStartTime, bHasWarned));
    9470         243 :         ++nCurBand;
    9471             :     }
    9472         284 :     if (iDim > 0)
    9473         101 :         goto lbl_return_to_caller;
    9474             : 
    9475         183 :     if (!array->GetFilename().empty())
    9476             :     {
    9477         165 :         poDS->SetPhysicalFilename(array->GetFilename().c_str());
    9478             :         std::string osDerivedDatasetName(
    9479             :             CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
    9480         330 :                        int(iYDim), array->GetFullName().c_str()));
    9481         165 :         if (!array->GetContext().empty())
    9482             :         {
    9483           2 :             osDerivedDatasetName += " with context ";
    9484           2 :             osDerivedDatasetName += array->GetContext();
    9485             :         }
    9486         165 :         poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
    9487         165 :         poDS->TryLoadXML();
    9488             : 
    9489           2 :         for (const auto &[pszKey, pszValue] :
    9490             :              cpl::IterateNameValue(static_cast<CSLConstList>(
    9491         167 :                  poDS->GDALPamDataset::GetMetadata())))
    9492             :         {
    9493           1 :             poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
    9494             :         }
    9495             :     }
    9496             : 
    9497         183 :     return poDS.release();
    9498             : }
    9499             : 
    9500             : /************************************************************************/
    9501             : /*                          AsClassicDataset()                         */
    9502             : /************************************************************************/
    9503             : 
    9504             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
    9505             :  *
    9506             :  * In the case of > 2D arrays, additional dimensions will be represented as
    9507             :  * raster bands.
    9508             :  *
    9509             :  * The "reverse" method is GDALRasterBand::AsMDArray().
    9510             :  *
    9511             :  * This is the same as the C function GDALMDArrayAsClassicDataset().
    9512             :  *
    9513             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
    9514             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
    9515             :  *              Ignored if the dimension count is 1.
    9516             :  * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
    9517             :  *                    option.
    9518             :  * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
    9519             :  *                     nullptr. Current supported options are:
    9520             :  *                     <ul>
    9521             :  *                     <li>BAND_METADATA: JSON serialized array defining which
    9522             :  *                         arrays of the poRootGroup, indexed by non-X and Y
    9523             :  *                         dimensions, should be mapped as band metadata items.
    9524             :  *                         Each array item should be an object with the
    9525             :  *                         following members:
    9526             :  *                         - "array": full name of a band parameter array.
    9527             :  *                           Such array must be a one
    9528             :  *                           dimensional array, and its dimension must be one of
    9529             :  *                           the dimensions of the array on which the method is
    9530             :  *                           called (excluding the X and Y dimensons).
    9531             :  *                         - "item_name": band metadata item name
    9532             :  *                         - "item_value": (optional) String, where "%[x][.y]f",
    9533             :  *                           "%[x][.y]g" or "%s" printf-like formatting can be
    9534             :  *                           used to format the corresponding value of the
    9535             :  *                           parameter array. The percentage character should be
    9536             :  *                           repeated: "%%"
    9537             :  *                           "${attribute_name}" can also be used to include the
    9538             :  *                           value of an attribute for the array.
    9539             :  *                           If "item_value" is not provided, a default formatting
    9540             :  *                           of the value will be applied.
    9541             :  *
    9542             :  *                         Example:
    9543             :  *                         [
    9544             :  *                            {
    9545             :  *                              "array": "/sensor_band_parameters/wavelengths",
    9546             :  *                              "item_name": "WAVELENGTH",
    9547             :  *                              "item_value": "%.1f ${units}"
    9548             :  *                            },
    9549             :  *                            {
    9550             :  *                              "array": "/sensor_band_parameters/fwhm",
    9551             :  *                              "item_name": "FWHM"
    9552             :  *                            },
    9553             :  *                            {
    9554             :  *                              "array": "/sensor_band_parameters/fwhm",
    9555             :  *                              "item_name": "FWHM_UNIT",
    9556             :  *                              "item_value": "${units}"
    9557             :  *                            }
    9558             :  *                         ]
    9559             :  *                     </li>
    9560             :  *                     <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
    9561             :  *                         seconds allowed to set the DIM_{dimname}_VALUE band
    9562             :  *                         metadata items from the indexing variable of the
    9563             :  *                         dimensions.
    9564             :  *                         Default value is 5. 'unlimited' can be used to mean
    9565             :  *                         unlimited delay. Can also be defined globally with
    9566             :  *                         the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
    9567             :  *                         option.</li>
    9568             :  *                     </ul>
    9569             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
    9570             :  */
    9571             : GDALDataset *
    9572         206 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
    9573             :                               const std::shared_ptr<GDALGroup> &poRootGroup,
    9574             :                               CSLConstList papszOptions) const
    9575             : {
    9576         412 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    9577         206 :     if (!self)
    9578             :     {
    9579           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    9580             :                  "Driver implementation issue: m_pSelf not set !");
    9581           0 :         return nullptr;
    9582             :     }
    9583         206 :     return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
    9584         206 :                                         papszOptions);
    9585             : }
    9586             : 
    9587             : /************************************************************************/
    9588             : /*                           GetStatistics()                            */
    9589             : /************************************************************************/
    9590             : 
    9591             : /**
    9592             :  * \brief Fetch statistics.
    9593             :  *
    9594             :  * Returns the minimum, maximum, mean and standard deviation of all
    9595             :  * pixel values in this array.
    9596             :  *
    9597             :  * If bForce is FALSE results will only be returned if it can be done
    9598             :  * quickly (i.e. without scanning the data).  If bForce is FALSE and
    9599             :  * results cannot be returned efficiently, the method will return CE_Warning
    9600             :  * but no warning will have been issued.   This is a non-standard use of
    9601             :  * the CE_Warning return value to indicate "nothing done".
    9602             :  *
    9603             :  * When cached statistics are not available, and bForce is TRUE,
    9604             :  * ComputeStatistics() is called.
    9605             :  *
    9606             :  * Note that file formats using PAM (Persistent Auxiliary Metadata) services
    9607             :  * will generally cache statistics in the .aux.xml file allowing fast fetch
    9608             :  * after the first request.
    9609             :  *
    9610             :  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
    9611             :  *
    9612             :  * This method is the same as the C function GDALMDArrayGetStatistics().
    9613             :  *
    9614             :  * @param bApproxOK Currently ignored. In the future, should be set to true
    9615             :  * if statistics on the whole array are wished, or to false if a subset of it
    9616             :  * may be used.
    9617             :  *
    9618             :  * @param bForce If false statistics will only be returned if it can
    9619             :  * be done without rescanning the image.
    9620             :  *
    9621             :  * @param pdfMin Location into which to load image minimum (may be NULL).
    9622             :  *
    9623             :  * @param pdfMax Location into which to load image maximum (may be NULL).-
    9624             :  *
    9625             :  * @param pdfMean Location into which to load image mean (may be NULL).
    9626             :  *
    9627             :  * @param pdfStdDev Location into which to load image standard deviation
    9628             :  * (may be NULL).
    9629             :  *
    9630             :  * @param pnValidCount Number of samples whose value is different from the
    9631             :  * nodata value. (may be NULL)
    9632             :  *
    9633             :  * @param pfnProgress a function to call to report progress, or NULL.
    9634             :  *
    9635             :  * @param pProgressData application data to pass to the progress function.
    9636             :  *
    9637             :  * @return CE_None on success, CE_Warning if no values returned,
    9638             :  * CE_Failure if an error occurs.
    9639             :  *
    9640             :  * @since GDAL 3.2
    9641             :  */
    9642             : 
    9643           7 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
    9644             :                                   double *pdfMax, double *pdfMean,
    9645             :                                   double *pdfStdDev, GUInt64 *pnValidCount,
    9646             :                                   GDALProgressFunc pfnProgress,
    9647             :                                   void *pProgressData)
    9648             : {
    9649           7 :     if (!bForce)
    9650           1 :         return CE_Warning;
    9651             : 
    9652          12 :     return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
    9653           6 :                              pnValidCount, pfnProgress, pProgressData, nullptr)
    9654           6 :                ? CE_None
    9655           6 :                : CE_Failure;
    9656             : }
    9657             : 
    9658             : /************************************************************************/
    9659             : /*                         ComputeStatistics()                          */
    9660             : /************************************************************************/
    9661             : 
    9662             : /**
    9663             :  * \brief Compute statistics.
    9664             :  *
    9665             :  * Returns the minimum, maximum, mean and standard deviation of all
    9666             :  * pixel values in this array.
    9667             :  *
    9668             :  * Pixels taken into account in statistics are those whose mask value
    9669             :  * (as determined by GetMask()) is non-zero.
    9670             :  *
    9671             :  * Once computed, the statistics will generally be "set" back on the
    9672             :  * owing dataset.
    9673             :  *
    9674             :  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
    9675             :  *
    9676             :  * This method is the same as the C functions GDALMDArrayComputeStatistics().
    9677             :  * and GDALMDArrayComputeStatisticsEx().
    9678             :  *
    9679             :  * @param bApproxOK Currently ignored. In the future, should be set to true
    9680             :  * if statistics on the whole array are wished, or to false if a subset of it
    9681             :  * may be used.
    9682             :  *
    9683             :  * @param pdfMin Location into which to load image minimum (may be NULL).
    9684             :  *
    9685             :  * @param pdfMax Location into which to load image maximum (may be NULL).-
    9686             :  *
    9687             :  * @param pdfMean Location into which to load image mean (may be NULL).
    9688             :  *
    9689             :  * @param pdfStdDev Location into which to load image standard deviation
    9690             :  * (may be NULL).
    9691             :  *
    9692             :  * @param pnValidCount Number of samples whose value is different from the
    9693             :  * nodata value. (may be NULL)
    9694             :  *
    9695             :  * @param pfnProgress a function to call to report progress, or NULL.
    9696             :  *
    9697             :  * @param pProgressData application data to pass to the progress function.
    9698             :  *
    9699             :  * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
    9700             :  *                     Options are driver specific. For now the netCDF and Zarr
    9701             :  *                     drivers recognize UPDATE_METADATA=YES, whose effect is
    9702             :  *                     to add or update the actual_range attribute with the
    9703             :  *                     computed min/max, only if done on the full array, in non
    9704             :  *                     approximate mode, and the dataset is opened in update
    9705             :  *                     mode.
    9706             :  *
    9707             :  * @return true on success
    9708             :  *
    9709             :  * @since GDAL 3.2
    9710             :  */
    9711             : 
    9712          10 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
    9713             :                                     double *pdfMax, double *pdfMean,
    9714             :                                     double *pdfStdDev, GUInt64 *pnValidCount,
    9715             :                                     GDALProgressFunc pfnProgress,
    9716             :                                     void *pProgressData,
    9717             :                                     CSLConstList papszOptions)
    9718             : {
    9719             :     struct StatsPerChunkType
    9720             :     {
    9721             :         const GDALMDArray *array = nullptr;
    9722             :         std::shared_ptr<GDALMDArray> poMask{};
    9723             :         double dfMin = std::numeric_limits<double>::max();
    9724             :         double dfMax = -std::numeric_limits<double>::max();
    9725             :         double dfMean = 0.0;
    9726             :         double dfM2 = 0.0;
    9727             :         GUInt64 nValidCount = 0;
    9728             :         std::vector<GByte> abyData{};
    9729             :         std::vector<double> adfData{};
    9730             :         std::vector<GByte> abyMaskData{};
    9731             :         GDALProgressFunc pfnProgress = nullptr;
    9732             :         void *pProgressData = nullptr;
    9733             :     };
    9734             : 
    9735          10 :     const auto PerChunkFunc = [](GDALAbstractMDArray *,
    9736             :                                  const GUInt64 *chunkArrayStartIdx,
    9737             :                                  const size_t *chunkCount, GUInt64 iCurChunk,
    9738             :                                  GUInt64 nChunkCount, void *pUserData)
    9739             :     {
    9740          10 :         StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
    9741          10 :         const GDALMDArray *array = data->array;
    9742          10 :         const GDALMDArray *poMask = data->poMask.get();
    9743          10 :         const size_t nDims = array->GetDimensionCount();
    9744          10 :         size_t nVals = 1;
    9745          27 :         for (size_t i = 0; i < nDims; i++)
    9746          17 :             nVals *= chunkCount[i];
    9747             : 
    9748             :         // Get mask
    9749          10 :         data->abyMaskData.resize(nVals);
    9750          10 :         if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
    9751          10 :                            poMask->GetDataType(), &data->abyMaskData[0])))
    9752             :         {
    9753           0 :             return false;
    9754             :         }
    9755             : 
    9756             :         // Get data
    9757          10 :         const auto &oType = array->GetDataType();
    9758          10 :         if (oType.GetNumericDataType() == GDT_Float64)
    9759             :         {
    9760           4 :             data->adfData.resize(nVals);
    9761           4 :             if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
    9762           4 :                              oType, &data->adfData[0]))
    9763             :             {
    9764           0 :                 return false;
    9765             :             }
    9766             :         }
    9767             :         else
    9768             :         {
    9769           6 :             data->abyData.resize(nVals * oType.GetSize());
    9770           6 :             if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
    9771           6 :                              oType, &data->abyData[0]))
    9772             :             {
    9773           0 :                 return false;
    9774             :             }
    9775           6 :             data->adfData.resize(nVals);
    9776           6 :             GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
    9777           6 :                             static_cast<int>(oType.GetSize()),
    9778           6 :                             &data->adfData[0], GDT_Float64,
    9779             :                             static_cast<int>(sizeof(double)),
    9780             :                             static_cast<GPtrDiff_t>(nVals));
    9781             :         }
    9782         469 :         for (size_t i = 0; i < nVals; i++)
    9783             :         {
    9784         459 :             if (data->abyMaskData[i])
    9785             :             {
    9786         454 :                 const double dfValue = data->adfData[i];
    9787         454 :                 data->dfMin = std::min(data->dfMin, dfValue);
    9788         454 :                 data->dfMax = std::max(data->dfMax, dfValue);
    9789         454 :                 data->nValidCount++;
    9790         454 :                 const double dfDelta = dfValue - data->dfMean;
    9791         454 :                 data->dfMean += dfDelta / data->nValidCount;
    9792         454 :                 data->dfM2 += dfDelta * (dfValue - data->dfMean);
    9793             :             }
    9794             :         }
    9795          10 :         if (data->pfnProgress &&
    9796           0 :             !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
    9797             :                                "", data->pProgressData))
    9798             :         {
    9799           0 :             return false;
    9800             :         }
    9801          10 :         return true;
    9802             :     };
    9803             : 
    9804          10 :     const auto &oType = GetDataType();
    9805          20 :     if (oType.GetClass() != GEDTC_NUMERIC ||
    9806          10 :         GDALDataTypeIsComplex(oType.GetNumericDataType()))
    9807             :     {
    9808           0 :         CPLError(
    9809             :             CE_Failure, CPLE_NotSupported,
    9810             :             "Statistics can only be computed on non-complex numeric data type");
    9811           0 :         return false;
    9812             :     }
    9813             : 
    9814          10 :     const size_t nDims = GetDimensionCount();
    9815          20 :     std::vector<GUInt64> arrayStartIdx(nDims);
    9816          20 :     std::vector<GUInt64> count(nDims);
    9817          10 :     const auto &poDims = GetDimensions();
    9818          27 :     for (size_t i = 0; i < nDims; i++)
    9819             :     {
    9820          17 :         count[i] = poDims[i]->GetSize();
    9821             :     }
    9822          10 :     const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
    9823             :     const size_t nMaxChunkSize =
    9824             :         pszSwathSize
    9825          10 :             ? static_cast<size_t>(
    9826           0 :                   std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    9827           0 :                            CPLAtoGIntBig(pszSwathSize)))
    9828             :             : static_cast<size_t>(
    9829          10 :                   std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    9830          10 :                            GDALGetCacheMax64() / 4));
    9831          20 :     StatsPerChunkType sData;
    9832          10 :     sData.array = this;
    9833          10 :     sData.poMask = GetMask(nullptr);
    9834          10 :     if (sData.poMask == nullptr)
    9835             :     {
    9836           0 :         return false;
    9837             :     }
    9838          10 :     sData.pfnProgress = pfnProgress;
    9839          10 :     sData.pProgressData = pProgressData;
    9840          10 :     if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
    9841          20 :                          GetProcessingChunkSize(nMaxChunkSize).data(),
    9842          10 :                          PerChunkFunc, &sData))
    9843             :     {
    9844           0 :         return false;
    9845             :     }
    9846             : 
    9847          10 :     if (pdfMin)
    9848          10 :         *pdfMin = sData.dfMin;
    9849             : 
    9850          10 :     if (pdfMax)
    9851          10 :         *pdfMax = sData.dfMax;
    9852             : 
    9853          10 :     if (pdfMean)
    9854           8 :         *pdfMean = sData.dfMean;
    9855             : 
    9856             :     const double dfStdDev =
    9857          10 :         sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
    9858          10 :     if (pdfStdDev)
    9859           8 :         *pdfStdDev = dfStdDev;
    9860             : 
    9861          10 :     if (pnValidCount)
    9862           8 :         *pnValidCount = sData.nValidCount;
    9863             : 
    9864          10 :     SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
    9865          10 :                   sData.nValidCount, papszOptions);
    9866             : 
    9867          10 :     return true;
    9868             : }
    9869             : 
    9870             : /************************************************************************/
    9871             : /*                            SetStatistics()                           */
    9872             : /************************************************************************/
    9873             : //! @cond Doxygen_Suppress
    9874           5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
    9875             :                                 double /* dfMax */, double /* dfMean */,
    9876             :                                 double /* dfStdDev */,
    9877             :                                 GUInt64 /* nValidCount */,
    9878             :                                 CSLConstList /* papszOptions */)
    9879             : {
    9880           5 :     CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
    9881           5 :     return false;
    9882             : }
    9883             : 
    9884             : //! @endcond
    9885             : 
    9886             : /************************************************************************/
    9887             : /*                           ClearStatistics()                          */
    9888             : /************************************************************************/
    9889             : 
    9890             : /**
    9891             :  * \brief Clear statistics.
    9892             :  *
    9893             :  * @since GDAL 3.4
    9894             :  */
    9895           0 : void GDALMDArray::ClearStatistics()
    9896             : {
    9897           0 : }
    9898             : 
    9899             : /************************************************************************/
    9900             : /*                      GetCoordinateVariables()                        */
    9901             : /************************************************************************/
    9902             : 
    9903             : /**
    9904             :  * \brief Return coordinate variables.
    9905             :  *
    9906             :  * Coordinate variables are an alternate way of indexing an array that can
    9907             :  * be sometimes used. For example, an array collected through remote sensing
    9908             :  * might be indexed by (scanline, pixel). But there can be
    9909             :  * a longitude and latitude arrays alongside that are also both indexed by
    9910             :  * (scanline, pixel), and are referenced from operational arrays for
    9911             :  * reprojection purposes.
    9912             :  *
    9913             :  * For netCDF, this will return the arrays referenced by the "coordinates"
    9914             :  * attribute.
    9915             :  *
    9916             :  * This method is the same as the C function
    9917             :  * GDALMDArrayGetCoordinateVariables().
    9918             :  *
    9919             :  * @return a vector of arrays
    9920             :  *
    9921             :  * @since GDAL 3.4
    9922             :  */
    9923             : 
    9924             : std::vector<std::shared_ptr<GDALMDArray>>
    9925          13 : GDALMDArray::GetCoordinateVariables() const
    9926             : {
    9927          13 :     return {};
    9928             : }
    9929             : 
    9930             : /************************************************************************/
    9931             : /*                       ~GDALExtendedDataType()                        */
    9932             : /************************************************************************/
    9933             : 
    9934             : GDALExtendedDataType::~GDALExtendedDataType() = default;
    9935             : 
    9936             : /************************************************************************/
    9937             : /*                        GDALExtendedDataType()                        */
    9938             : /************************************************************************/
    9939             : 
    9940        8303 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
    9941        8303 :                                            GDALExtendedDataTypeSubType eSubType)
    9942             :     : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
    9943        8303 :       m_nMaxStringLength(nMaxStringLength)
    9944             : {
    9945        8303 : }
    9946             : 
    9947             : /************************************************************************/
    9948             : /*                        GDALExtendedDataType()                        */
    9949             : /************************************************************************/
    9950             : 
    9951       35256 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
    9952             :     : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
    9953       35256 :       m_nSize(GDALGetDataTypeSizeBytes(eType))
    9954             : {
    9955       35256 : }
    9956             : 
    9957             : /************************************************************************/
    9958             : /*                        GDALExtendedDataType()                        */
    9959             : /************************************************************************/
    9960             : 
    9961         632 : GDALExtendedDataType::GDALExtendedDataType(
    9962             :     const std::string &osName, size_t nTotalSize,
    9963         632 :     std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
    9964             :     : m_osName(osName), m_eClass(GEDTC_COMPOUND),
    9965         632 :       m_aoComponents(std::move(components)), m_nSize(nTotalSize)
    9966             : {
    9967         632 : }
    9968             : 
    9969             : /************************************************************************/
    9970             : /*                        GDALExtendedDataType()                        */
    9971             : /************************************************************************/
    9972             : 
    9973             : /** Copy constructor. */
    9974       15413 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
    9975       30826 :     : m_osName(other.m_osName), m_eClass(other.m_eClass),
    9976       15413 :       m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
    9977       15413 :       m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength)
    9978             : {
    9979       15413 :     if (m_eClass == GEDTC_COMPOUND)
    9980             :     {
    9981         431 :         for (const auto &elt : other.m_aoComponents)
    9982             :         {
    9983         281 :             m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
    9984             :         }
    9985             :     }
    9986       15413 : }
    9987             : 
    9988             : /************************************************************************/
    9989             : /*                            operator= ()                              */
    9990             : /************************************************************************/
    9991             : 
    9992             : /** Copy assignment. */
    9993             : GDALExtendedDataType &
    9994         606 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
    9995             : {
    9996         606 :     if (this != &other)
    9997             :     {
    9998         606 :         m_osName = other.m_osName;
    9999         606 :         m_eClass = other.m_eClass;
   10000         606 :         m_eSubType = other.m_eSubType;
   10001         606 :         m_eNumericDT = other.m_eNumericDT;
   10002         606 :         m_nSize = other.m_nSize;
   10003         606 :         m_nMaxStringLength = other.m_nMaxStringLength;
   10004         606 :         m_aoComponents.clear();
   10005         606 :         if (m_eClass == GEDTC_COMPOUND)
   10006             :         {
   10007           0 :             for (const auto &elt : other.m_aoComponents)
   10008             :             {
   10009           0 :                 m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
   10010             :             }
   10011             :         }
   10012             :     }
   10013         606 :     return *this;
   10014             : }
   10015             : 
   10016             : /************************************************************************/
   10017             : /*                            operator= ()                              */
   10018             : /************************************************************************/
   10019             : 
   10020             : /** Move assignment. */
   10021             : GDALExtendedDataType &
   10022       14541 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other)
   10023             : {
   10024       14541 :     m_osName = std::move(other.m_osName);
   10025       14541 :     m_eClass = other.m_eClass;
   10026       14541 :     m_eSubType = other.m_eSubType;
   10027       14541 :     m_eNumericDT = other.m_eNumericDT;
   10028       14541 :     m_nSize = other.m_nSize;
   10029       14541 :     m_nMaxStringLength = other.m_nMaxStringLength;
   10030       14541 :     m_aoComponents = std::move(other.m_aoComponents);
   10031       14541 :     other.m_eClass = GEDTC_NUMERIC;
   10032       14541 :     other.m_eNumericDT = GDT_Unknown;
   10033       14541 :     other.m_nSize = 0;
   10034       14541 :     other.m_nMaxStringLength = 0;
   10035       14541 :     return *this;
   10036             : }
   10037             : 
   10038             : /************************************************************************/
   10039             : /*                           Create()                                   */
   10040             : /************************************************************************/
   10041             : 
   10042             : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
   10043             :  *
   10044             :  * This is the same as the C function GDALExtendedDataTypeCreate()
   10045             :  *
   10046             :  * @param eType Numeric data type. Must be different from GDT_Unknown and
   10047             :  * GDT_TypeCount
   10048             :  */
   10049       35249 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
   10050             : {
   10051       35249 :     return GDALExtendedDataType(eType);
   10052             : }
   10053             : 
   10054             : /************************************************************************/
   10055             : /*                           Create()                                   */
   10056             : /************************************************************************/
   10057             : 
   10058             : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
   10059             :  *
   10060             :  * This is the same as the C function GDALExtendedDataTypeCreateCompound()
   10061             :  *
   10062             :  * @param osName Type name.
   10063             :  * @param nTotalSize Total size of the type in bytes.
   10064             :  *                   Should be large enough to store all components.
   10065             :  * @param components Components of the compound type.
   10066             :  */
   10067         639 : GDALExtendedDataType GDALExtendedDataType::Create(
   10068             :     const std::string &osName, size_t nTotalSize,
   10069             :     std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
   10070             : {
   10071         639 :     size_t nLastOffset = 0;
   10072             :     // Some arbitrary threshold to avoid potential integer overflows
   10073         639 :     if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
   10074             :     {
   10075           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10076           2 :         return GDALExtendedDataType(GDT_Unknown);
   10077             :     }
   10078        3155 :     for (const auto &comp : components)
   10079             :     {
   10080             :         // Check alignment too ?
   10081        2519 :         if (comp->GetOffset() < nLastOffset)
   10082             :         {
   10083           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10084           1 :             return GDALExtendedDataType(GDT_Unknown);
   10085             :         }
   10086        2518 :         nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
   10087             :     }
   10088         636 :     if (nTotalSize < nLastOffset)
   10089             :     {
   10090           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10091           1 :         return GDALExtendedDataType(GDT_Unknown);
   10092             :     }
   10093         635 :     if (nTotalSize == 0 || components.empty())
   10094             :     {
   10095           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
   10096           3 :         return GDALExtendedDataType(GDT_Unknown);
   10097             :     }
   10098         632 :     return GDALExtendedDataType(osName, nTotalSize, std::move(components));
   10099             : }
   10100             : 
   10101             : /************************************************************************/
   10102             : /*                           Create()                                   */
   10103             : /************************************************************************/
   10104             : 
   10105             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   10106             :  *
   10107             :  * This is the same as the C function GDALExtendedDataTypeCreateString().
   10108             :  *
   10109             :  * @param nMaxStringLength maximum length of a string in bytes. 0 if
   10110             :  * unknown/unlimited
   10111             :  * @param eSubType Subtype.
   10112             :  */
   10113             : GDALExtendedDataType
   10114        8303 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
   10115             :                                    GDALExtendedDataTypeSubType eSubType)
   10116             : {
   10117        8303 :     return GDALExtendedDataType(nMaxStringLength, eSubType);
   10118             : }
   10119             : 
   10120             : /************************************************************************/
   10121             : /*                           operator==()                               */
   10122             : /************************************************************************/
   10123             : 
   10124             : /** Equality operator.
   10125             :  *
   10126             :  * This is the same as the C function GDALExtendedDataTypeEquals().
   10127             :  */
   10128        2240 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
   10129             : {
   10130        2213 :     if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
   10131        4453 :         m_nSize != other.m_nSize || m_osName != other.m_osName)
   10132             :     {
   10133         177 :         return false;
   10134             :     }
   10135        2063 :     if (m_eClass == GEDTC_NUMERIC)
   10136             :     {
   10137         866 :         return m_eNumericDT == other.m_eNumericDT;
   10138             :     }
   10139        1197 :     if (m_eClass == GEDTC_STRING)
   10140             :     {
   10141        1016 :         return true;
   10142             :     }
   10143         181 :     CPLAssert(m_eClass == GEDTC_COMPOUND);
   10144         181 :     if (m_aoComponents.size() != other.m_aoComponents.size())
   10145             :     {
   10146           2 :         return false;
   10147             :     }
   10148         806 :     for (size_t i = 0; i < m_aoComponents.size(); i++)
   10149             :     {
   10150         627 :         if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
   10151             :         {
   10152           0 :             return false;
   10153             :         }
   10154             :     }
   10155         179 :     return true;
   10156             : }
   10157             : 
   10158             : /************************************************************************/
   10159             : /*                        CanConvertTo()                                */
   10160             : /************************************************************************/
   10161             : 
   10162             : /** Return whether this data type can be converted to the other one.
   10163             :  *
   10164             :  * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
   10165             :  *
   10166             :  * @param other Target data type for the conversion being considered.
   10167             :  */
   10168        8087 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
   10169             : {
   10170        8087 :     if (m_eClass == GEDTC_NUMERIC)
   10171             :     {
   10172        5737 :         if (m_eNumericDT == GDT_Unknown)
   10173           0 :             return false;
   10174        5737 :         if (other.m_eClass == GEDTC_NUMERIC &&
   10175        5650 :             other.m_eNumericDT == GDT_Unknown)
   10176           0 :             return false;
   10177        5824 :         return other.m_eClass == GEDTC_NUMERIC ||
   10178        5824 :                other.m_eClass == GEDTC_STRING;
   10179             :     }
   10180        2350 :     if (m_eClass == GEDTC_STRING)
   10181             :     {
   10182        2211 :         return other.m_eClass == m_eClass;
   10183             :     }
   10184         139 :     CPLAssert(m_eClass == GEDTC_COMPOUND);
   10185         139 :     if (other.m_eClass != GEDTC_COMPOUND)
   10186           0 :         return false;
   10187             :     std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
   10188         278 :         srcComponents;
   10189         568 :     for (const auto &srcComp : m_aoComponents)
   10190             :     {
   10191         429 :         srcComponents[srcComp->GetName()] = &srcComp;
   10192             :     }
   10193         419 :     for (const auto &dstComp : other.m_aoComponents)
   10194             :     {
   10195         281 :         auto oIter = srcComponents.find(dstComp->GetName());
   10196         281 :         if (oIter == srcComponents.end())
   10197           1 :             return false;
   10198         280 :         if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
   10199           0 :             return false;
   10200             :     }
   10201         138 :     return true;
   10202             : }
   10203             : 
   10204             : /************************************************************************/
   10205             : /*                     NeedsFreeDynamicMemory()                         */
   10206             : /************************************************************************/
   10207             : 
   10208             : /** Return whether the data type holds dynamically allocated memory, that
   10209             :  * needs to be freed with FreeDynamicMemory().
   10210             :  *
   10211             :  */
   10212        3501 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
   10213             : {
   10214        3501 :     switch (m_eClass)
   10215             :     {
   10216         843 :         case GEDTC_STRING:
   10217         843 :             return true;
   10218             : 
   10219        2585 :         case GEDTC_NUMERIC:
   10220        2585 :             return false;
   10221             : 
   10222          73 :         case GEDTC_COMPOUND:
   10223             :         {
   10224         186 :             for (const auto &comp : m_aoComponents)
   10225             :             {
   10226         172 :                 if (comp->GetType().NeedsFreeDynamicMemory())
   10227          59 :                     return true;
   10228             :             }
   10229             :         }
   10230             :     }
   10231          14 :     return false;
   10232             : }
   10233             : 
   10234             : /************************************************************************/
   10235             : /*                        FreeDynamicMemory()                           */
   10236             : /************************************************************************/
   10237             : 
   10238             : /** Release the dynamic memory (strings typically) from a raw value.
   10239             :  *
   10240             :  * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
   10241             :  *
   10242             :  * @param pBuffer Raw buffer of a single element of an attribute or array value.
   10243             :  */
   10244        3258 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
   10245             : {
   10246        3258 :     switch (m_eClass)
   10247             :     {
   10248        2284 :         case GEDTC_STRING:
   10249             :         {
   10250             :             char *pszStr;
   10251        2284 :             memcpy(&pszStr, pBuffer, sizeof(char *));
   10252        2284 :             if (pszStr)
   10253             :             {
   10254        1780 :                 VSIFree(pszStr);
   10255             :             }
   10256        2284 :             break;
   10257             :         }
   10258             : 
   10259         835 :         case GEDTC_NUMERIC:
   10260             :         {
   10261         835 :             break;
   10262             :         }
   10263             : 
   10264         139 :         case GEDTC_COMPOUND:
   10265             :         {
   10266         139 :             GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
   10267         605 :             for (const auto &comp : m_aoComponents)
   10268             :             {
   10269         932 :                 comp->GetType().FreeDynamicMemory(pabyBuffer +
   10270         466 :                                                   comp->GetOffset());
   10271             :             }
   10272         139 :             break;
   10273             :         }
   10274             :     }
   10275        3258 : }
   10276             : 
   10277             : /************************************************************************/
   10278             : /*                      ~GDALEDTComponent()                             */
   10279             : /************************************************************************/
   10280             : 
   10281             : GDALEDTComponent::~GDALEDTComponent() = default;
   10282             : 
   10283             : /************************************************************************/
   10284             : /*                      GDALEDTComponent()                              */
   10285             : /************************************************************************/
   10286             : 
   10287             : /** constructor of a GDALEDTComponent
   10288             :  *
   10289             :  * This is the same as the C function GDALEDTComponendCreate()
   10290             :  *
   10291             :  * @param name Component name
   10292             :  * @param offset Offset in byte of the component in the compound data type.
   10293             :  *               In case of nesting of compound data type, this should be
   10294             :  *               the offset to the immediate belonging data type, not to the
   10295             :  *               higher level one.
   10296             :  * @param type   Component data type.
   10297             :  */
   10298        2510 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
   10299        2510 :                                    const GDALExtendedDataType &type)
   10300        2510 :     : m_osName(name), m_nOffset(offset), m_oType(type)
   10301             : {
   10302        2510 : }
   10303             : 
   10304             : /************************************************************************/
   10305             : /*                      GDALEDTComponent()                              */
   10306             : /************************************************************************/
   10307             : 
   10308             : /** Copy constructor. */
   10309             : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
   10310             : 
   10311             : /************************************************************************/
   10312             : /*                           operator==()                               */
   10313             : /************************************************************************/
   10314             : 
   10315             : /** Equality operator.
   10316             :  */
   10317         627 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
   10318             : {
   10319        1254 :     return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
   10320        1254 :            m_oType == other.m_oType;
   10321             : }
   10322             : 
   10323             : /************************************************************************/
   10324             : /*                        ~GDALDimension()                              */
   10325             : /************************************************************************/
   10326             : 
   10327             : GDALDimension::~GDALDimension() = default;
   10328             : 
   10329             : /************************************************************************/
   10330             : /*                         GDALDimension()                              */
   10331             : /************************************************************************/
   10332             : 
   10333             : //! @cond Doxygen_Suppress
   10334             : /** Constructor.
   10335             :  *
   10336             :  * @param osParentName Parent name
   10337             :  * @param osName name
   10338             :  * @param osType type. See GetType().
   10339             :  * @param osDirection direction. See GetDirection().
   10340             :  * @param nSize size.
   10341             :  */
   10342        8092 : GDALDimension::GDALDimension(const std::string &osParentName,
   10343             :                              const std::string &osName,
   10344             :                              const std::string &osType,
   10345        8092 :                              const std::string &osDirection, GUInt64 nSize)
   10346             :     : m_osName(osName),
   10347             :       m_osFullName(
   10348        8092 :           !osParentName.empty()
   10349       11960 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
   10350             :               : osName),
   10351       28144 :       m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
   10352             : {
   10353        8092 : }
   10354             : 
   10355             : //! @endcond
   10356             : 
   10357             : /************************************************************************/
   10358             : /*                         GetIndexingVariable()                        */
   10359             : /************************************************************************/
   10360             : 
   10361             : /** Return the variable that is used to index the dimension (if there is one).
   10362             :  *
   10363             :  * This is the array, typically one-dimensional, describing the values taken
   10364             :  * by the dimension.
   10365             :  */
   10366          49 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
   10367             : {
   10368          49 :     return nullptr;
   10369             : }
   10370             : 
   10371             : /************************************************************************/
   10372             : /*                         SetIndexingVariable()                        */
   10373             : /************************************************************************/
   10374             : 
   10375             : /** Set the variable that is used to index the dimension.
   10376             :  *
   10377             :  * This is the array, typically one-dimensional, describing the values taken
   10378             :  * by the dimension.
   10379             :  *
   10380             :  * Optionally implemented by drivers.
   10381             :  *
   10382             :  * Drivers known to implement it: MEM.
   10383             :  *
   10384             :  * @param poArray Variable to use to index the dimension.
   10385             :  * @return true in case of success.
   10386             :  */
   10387           3 : bool GDALDimension::SetIndexingVariable(
   10388             :     CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
   10389             : {
   10390           3 :     CPLError(CE_Failure, CPLE_NotSupported,
   10391             :              "SetIndexingVariable() not implemented");
   10392           3 :     return false;
   10393             : }
   10394             : 
   10395             : /************************************************************************/
   10396             : /*                            Rename()                                  */
   10397             : /************************************************************************/
   10398             : 
   10399             : /** Rename the dimension.
   10400             :  *
   10401             :  * This is not implemented by all drivers.
   10402             :  *
   10403             :  * Drivers known to implement it: MEM, netCDF, ZARR.
   10404             :  *
   10405             :  * This is the same as the C function GDALDimensionRename().
   10406             :  *
   10407             :  * @param osNewName New name.
   10408             :  *
   10409             :  * @return true in case of success
   10410             :  * @since GDAL 3.8
   10411             :  */
   10412           0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
   10413             : {
   10414           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
   10415           0 :     return false;
   10416             : }
   10417             : 
   10418             : /************************************************************************/
   10419             : /*                         BaseRename()                                 */
   10420             : /************************************************************************/
   10421             : 
   10422             : //! @cond Doxygen_Suppress
   10423           8 : void GDALDimension::BaseRename(const std::string &osNewName)
   10424             : {
   10425           8 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
   10426           8 :     m_osFullName += osNewName;
   10427           8 :     m_osName = osNewName;
   10428           8 : }
   10429             : 
   10430             : //! @endcond
   10431             : 
   10432             : //! @cond Doxygen_Suppress
   10433             : /************************************************************************/
   10434             : /*                          ParentRenamed()                             */
   10435             : /************************************************************************/
   10436             : 
   10437           8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
   10438             : {
   10439           8 :     m_osFullName = osNewParentFullName;
   10440           8 :     m_osFullName += "/";
   10441           8 :     m_osFullName += m_osName;
   10442           8 : }
   10443             : 
   10444             : //! @endcond
   10445             : 
   10446             : //! @cond Doxygen_Suppress
   10447             : /************************************************************************/
   10448             : /*                          ParentDeleted()                             */
   10449             : /************************************************************************/
   10450             : 
   10451           4 : void GDALDimension::ParentDeleted()
   10452             : {
   10453           4 : }
   10454             : 
   10455             : //! @endcond
   10456             : 
   10457             : /************************************************************************/
   10458             : /************************************************************************/
   10459             : /************************************************************************/
   10460             : /*                              C API                                   */
   10461             : /************************************************************************/
   10462             : /************************************************************************/
   10463             : /************************************************************************/
   10464             : 
   10465             : /************************************************************************/
   10466             : /*                      GDALExtendedDataTypeCreate()                    */
   10467             : /************************************************************************/
   10468             : 
   10469             : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
   10470             :  *
   10471             :  * This is the same as the C++ method GDALExtendedDataType::Create()
   10472             :  *
   10473             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10474             :  *
   10475             :  * @param eType Numeric data type. Must be different from GDT_Unknown and
   10476             :  * GDT_TypeCount
   10477             :  *
   10478             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10479             :  */
   10480        1937 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
   10481             : {
   10482        1937 :     if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
   10483             :     {
   10484           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
   10485             :                  "Illegal GDT_Unknown/GDT_TypeCount argument");
   10486           0 :         return nullptr;
   10487             :     }
   10488             :     return new GDALExtendedDataTypeHS(
   10489        1937 :         new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
   10490             : }
   10491             : 
   10492             : /************************************************************************/
   10493             : /*                    GDALExtendedDataTypeCreateString()                */
   10494             : /************************************************************************/
   10495             : 
   10496             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   10497             :  *
   10498             :  * This is the same as the C++ method GDALExtendedDataType::CreateString()
   10499             :  *
   10500             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10501             :  *
   10502             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10503             :  */
   10504           0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
   10505             : {
   10506           0 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   10507           0 :         GDALExtendedDataType::CreateString(nMaxStringLength)));
   10508             : }
   10509             : 
   10510             : /************************************************************************/
   10511             : /*                   GDALExtendedDataTypeCreateStringEx()               */
   10512             : /************************************************************************/
   10513             : 
   10514             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   10515             :  *
   10516             :  * This is the same as the C++ method GDALExtendedDataType::CreateString()
   10517             :  *
   10518             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10519             :  *
   10520             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10521             :  * @since GDAL 3.4
   10522             :  */
   10523             : GDALExtendedDataTypeH
   10524         187 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
   10525             :                                    GDALExtendedDataTypeSubType eSubType)
   10526             : {
   10527         187 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   10528         187 :         GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
   10529             : }
   10530             : 
   10531             : /************************************************************************/
   10532             : /*                   GDALExtendedDataTypeCreateCompound()               */
   10533             : /************************************************************************/
   10534             : 
   10535             : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
   10536             :  *
   10537             :  * This is the same as the C++ method GDALExtendedDataType::Create(const
   10538             :  * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
   10539             :  *
   10540             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10541             :  *
   10542             :  * @param pszName Type name.
   10543             :  * @param nTotalSize Total size of the type in bytes.
   10544             :  *                   Should be large enough to store all components.
   10545             :  * @param nComponents Number of components in comps array.
   10546             :  * @param comps Components.
   10547             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10548             :  */
   10549             : GDALExtendedDataTypeH
   10550          22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
   10551             :                                    size_t nComponents,
   10552             :                                    const GDALEDTComponentH *comps)
   10553             : {
   10554          44 :     std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
   10555          54 :     for (size_t i = 0; i < nComponents; i++)
   10556             :     {
   10557          64 :         compsCpp.emplace_back(std::unique_ptr<GDALEDTComponent>(
   10558          64 :             new GDALEDTComponent(*(comps[i]->m_poImpl.get()))));
   10559             :     }
   10560             :     auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
   10561          66 :                                            std::move(compsCpp));
   10562          22 :     if (dt.GetClass() != GEDTC_COMPOUND)
   10563           6 :         return nullptr;
   10564          16 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(dt));
   10565             : }
   10566             : 
   10567             : /************************************************************************/
   10568             : /*                     GDALExtendedDataTypeRelease()                    */
   10569             : /************************************************************************/
   10570             : 
   10571             : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
   10572             :  *
   10573             :  * Note: when applied on a object coming from a driver, this does not
   10574             :  * destroy the object in the file, database, etc...
   10575             :  */
   10576        6455 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
   10577             : {
   10578        6455 :     delete hEDT;
   10579        6455 : }
   10580             : 
   10581             : /************************************************************************/
   10582             : /*                     GDALExtendedDataTypeGetName()                    */
   10583             : /************************************************************************/
   10584             : 
   10585             : /** Return type name.
   10586             :  *
   10587             :  * This is the same as the C++ method GDALExtendedDataType::GetName()
   10588             :  */
   10589           7 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
   10590             : {
   10591           7 :     VALIDATE_POINTER1(hEDT, __func__, "");
   10592           7 :     return hEDT->m_poImpl->GetName().c_str();
   10593             : }
   10594             : 
   10595             : /************************************************************************/
   10596             : /*                     GDALExtendedDataTypeGetClass()                    */
   10597             : /************************************************************************/
   10598             : 
   10599             : /** Return type class.
   10600             :  *
   10601             :  * This is the same as the C++ method GDALExtendedDataType::GetClass()
   10602             :  */
   10603             : GDALExtendedDataTypeClass
   10604        7493 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
   10605             : {
   10606        7493 :     VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
   10607        7493 :     return hEDT->m_poImpl->GetClass();
   10608             : }
   10609             : 
   10610             : /************************************************************************/
   10611             : /*               GDALExtendedDataTypeGetNumericDataType()               */
   10612             : /************************************************************************/
   10613             : 
   10614             : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
   10615             :  *
   10616             :  * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
   10617             :  */
   10618         584 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
   10619             : {
   10620         584 :     VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
   10621         584 :     return hEDT->m_poImpl->GetNumericDataType();
   10622             : }
   10623             : 
   10624             : /************************************************************************/
   10625             : /*                   GDALExtendedDataTypeGetSize()                      */
   10626             : /************************************************************************/
   10627             : 
   10628             : /** Return data type size in bytes.
   10629             :  *
   10630             :  * This is the same as the C++ method GDALExtendedDataType::GetSize()
   10631             :  */
   10632        2491 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
   10633             : {
   10634        2491 :     VALIDATE_POINTER1(hEDT, __func__, 0);
   10635        2491 :     return hEDT->m_poImpl->GetSize();
   10636             : }
   10637             : 
   10638             : /************************************************************************/
   10639             : /*              GDALExtendedDataTypeGetMaxStringLength()                */
   10640             : /************************************************************************/
   10641             : 
   10642             : /** Return the maximum length of a string in bytes.
   10643             :  *
   10644             :  * 0 indicates unknown/unlimited string.
   10645             :  *
   10646             :  * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
   10647             :  */
   10648           3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
   10649             : {
   10650           3 :     VALIDATE_POINTER1(hEDT, __func__, 0);
   10651           3 :     return hEDT->m_poImpl->GetMaxStringLength();
   10652             : }
   10653             : 
   10654             : /************************************************************************/
   10655             : /*                    GDALExtendedDataTypeCanConvertTo()                */
   10656             : /************************************************************************/
   10657             : 
   10658             : /** Return whether this data type can be converted to the other one.
   10659             :  *
   10660             :  * This is the same as the C function GDALExtendedDataType::CanConvertTo()
   10661             :  *
   10662             :  * @param hSourceEDT Source data type for the conversion being considered.
   10663             :  * @param hTargetEDT Target data type for the conversion being considered.
   10664             :  * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
   10665             :  */
   10666           7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
   10667             :                                      GDALExtendedDataTypeH hTargetEDT)
   10668             : {
   10669           7 :     VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
   10670           7 :     VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
   10671           7 :     return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
   10672             : }
   10673             : 
   10674             : /************************************************************************/
   10675             : /*                        GDALExtendedDataTypeEquals()                  */
   10676             : /************************************************************************/
   10677             : 
   10678             : /** Return whether this data type is equal to another one.
   10679             :  *
   10680             :  * This is the same as the C++ method GDALExtendedDataType::operator==()
   10681             :  *
   10682             :  * @param hFirstEDT First data type.
   10683             :  * @param hSecondEDT Second data type.
   10684             :  * @return TRUE if they are equal. FALSE otherwise.
   10685             :  */
   10686          98 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
   10687             :                                GDALExtendedDataTypeH hSecondEDT)
   10688             : {
   10689          98 :     VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
   10690          98 :     VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
   10691          98 :     return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
   10692             : }
   10693             : 
   10694             : /************************************************************************/
   10695             : /*                    GDALExtendedDataTypeGetSubType()                  */
   10696             : /************************************************************************/
   10697             : 
   10698             : /** Return the subtype of a type.
   10699             :  *
   10700             :  * This is the same as the C++ method GDALExtendedDataType::GetSubType()
   10701             :  *
   10702             :  * @param hEDT Data type.
   10703             :  * @return subtype.
   10704             :  * @since 3.4
   10705             :  */
   10706             : GDALExtendedDataTypeSubType
   10707         104 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
   10708             : {
   10709         104 :     VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
   10710         104 :     return hEDT->m_poImpl->GetSubType();
   10711             : }
   10712             : 
   10713             : /************************************************************************/
   10714             : /*                     GDALExtendedDataTypeGetComponents()              */
   10715             : /************************************************************************/
   10716             : 
   10717             : /** Return the components of the data type (only valid when GetClass() ==
   10718             :  * GEDTC_COMPOUND)
   10719             :  *
   10720             :  * The returned array and its content must be freed with
   10721             :  * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
   10722             :  * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
   10723             :  * individual array members).
   10724             :  *
   10725             :  * This is the same as the C++ method GDALExtendedDataType::GetComponents()
   10726             :  *
   10727             :  * @param hEDT Data type
   10728             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   10729             :  * @return an array of *pnCount components.
   10730             :  */
   10731          44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
   10732             :                                                      size_t *pnCount)
   10733             : {
   10734          44 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   10735          44 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   10736          44 :     const auto &components = hEDT->m_poImpl->GetComponents();
   10737             :     auto ret = static_cast<GDALEDTComponentH *>(
   10738          44 :         CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
   10739         131 :     for (size_t i = 0; i < components.size(); i++)
   10740             :     {
   10741          87 :         ret[i] = new GDALEDTComponentHS(*components[i].get());
   10742             :     }
   10743          44 :     *pnCount = components.size();
   10744          44 :     return ret;
   10745             : }
   10746             : 
   10747             : /************************************************************************/
   10748             : /*                     GDALExtendedDataTypeFreeComponents()             */
   10749             : /************************************************************************/
   10750             : 
   10751             : /** Free the return of GDALExtendedDataTypeGetComponents().
   10752             :  *
   10753             :  * @param components return value of GDALExtendedDataTypeGetComponents()
   10754             :  * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
   10755             :  */
   10756          44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
   10757             :                                         size_t nCount)
   10758             : {
   10759         131 :     for (size_t i = 0; i < nCount; i++)
   10760             :     {
   10761          87 :         delete components[i];
   10762             :     }
   10763          44 :     CPLFree(components);
   10764          44 : }
   10765             : 
   10766             : /************************************************************************/
   10767             : /*                         GDALEDTComponentCreate()                     */
   10768             : /************************************************************************/
   10769             : 
   10770             : /** Create a new GDALEDTComponent.
   10771             :  *
   10772             :  * The returned value must be freed with GDALEDTComponentRelease().
   10773             :  *
   10774             :  * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
   10775             :  */
   10776          20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
   10777             :                                          GDALExtendedDataTypeH hType)
   10778             : {
   10779          20 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   10780          20 :     VALIDATE_POINTER1(hType, __func__, nullptr);
   10781             :     return new GDALEDTComponentHS(
   10782          20 :         GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
   10783             : }
   10784             : 
   10785             : /************************************************************************/
   10786             : /*                         GDALEDTComponentRelease()                    */
   10787             : /************************************************************************/
   10788             : 
   10789             : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
   10790             :  *
   10791             :  * Note: when applied on a object coming from a driver, this does not
   10792             :  * destroy the object in the file, database, etc...
   10793             :  */
   10794          61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
   10795             : {
   10796          61 :     delete hComp;
   10797          61 : }
   10798             : 
   10799             : /************************************************************************/
   10800             : /*                         GDALEDTComponentGetName()                    */
   10801             : /************************************************************************/
   10802             : 
   10803             : /** Return the name.
   10804             :  *
   10805             :  * The returned pointer is valid until hComp is released.
   10806             :  *
   10807             :  * This is the same as the C++ method GDALEDTComponent::GetName().
   10808             :  */
   10809          33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
   10810             : {
   10811          33 :     VALIDATE_POINTER1(hComp, __func__, nullptr);
   10812          33 :     return hComp->m_poImpl->GetName().c_str();
   10813             : }
   10814             : 
   10815             : /************************************************************************/
   10816             : /*                       GDALEDTComponentGetOffset()                    */
   10817             : /************************************************************************/
   10818             : 
   10819             : /** Return the offset (in bytes) of the component in the compound data type.
   10820             :  *
   10821             :  * This is the same as the C++ method GDALEDTComponent::GetOffset().
   10822             :  */
   10823          31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
   10824             : {
   10825          31 :     VALIDATE_POINTER1(hComp, __func__, 0);
   10826          31 :     return hComp->m_poImpl->GetOffset();
   10827             : }
   10828             : 
   10829             : /************************************************************************/
   10830             : /*                       GDALEDTComponentGetType()                      */
   10831             : /************************************************************************/
   10832             : 
   10833             : /** Return the data type of the component.
   10834             :  *
   10835             :  * This is the same as the C++ method GDALEDTComponent::GetType().
   10836             :  */
   10837          93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
   10838             : {
   10839          93 :     VALIDATE_POINTER1(hComp, __func__, nullptr);
   10840             :     return new GDALExtendedDataTypeHS(
   10841          93 :         new GDALExtendedDataType(hComp->m_poImpl->GetType()));
   10842             : }
   10843             : 
   10844             : /************************************************************************/
   10845             : /*                           GDALGroupRelease()                         */
   10846             : /************************************************************************/
   10847             : 
   10848             : /** Release the GDAL in-memory object associated with a GDALGroupH.
   10849             :  *
   10850             :  * Note: when applied on a object coming from a driver, this does not
   10851             :  * destroy the object in the file, database, etc...
   10852             :  */
   10853        1384 : void GDALGroupRelease(GDALGroupH hGroup)
   10854             : {
   10855        1384 :     delete hGroup;
   10856        1384 : }
   10857             : 
   10858             : /************************************************************************/
   10859             : /*                           GDALGroupGetName()                         */
   10860             : /************************************************************************/
   10861             : 
   10862             : /** Return the name of the group.
   10863             :  *
   10864             :  * The returned pointer is valid until hGroup is released.
   10865             :  *
   10866             :  * This is the same as the C++ method GDALGroup::GetName().
   10867             :  */
   10868          87 : const char *GDALGroupGetName(GDALGroupH hGroup)
   10869             : {
   10870          87 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10871          87 :     return hGroup->m_poImpl->GetName().c_str();
   10872             : }
   10873             : 
   10874             : /************************************************************************/
   10875             : /*                         GDALGroupGetFullName()                       */
   10876             : /************************************************************************/
   10877             : 
   10878             : /** Return the full name of the group.
   10879             :  *
   10880             :  * The returned pointer is valid until hGroup is released.
   10881             :  *
   10882             :  * This is the same as the C++ method GDALGroup::GetFullName().
   10883             :  */
   10884          41 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
   10885             : {
   10886          41 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10887          41 :     return hGroup->m_poImpl->GetFullName().c_str();
   10888             : }
   10889             : 
   10890             : /************************************************************************/
   10891             : /*                          GDALGroupGetMDArrayNames()                  */
   10892             : /************************************************************************/
   10893             : 
   10894             : /** Return the list of multidimensional array names contained in this group.
   10895             :  *
   10896             :  * This is the same as the C++ method GDALGroup::GetGroupNames().
   10897             :  *
   10898             :  * @return the array names, to be freed with CSLDestroy()
   10899             :  */
   10900         317 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
   10901             : {
   10902         317 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10903         634 :     auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
   10904         634 :     CPLStringList res;
   10905         805 :     for (const auto &name : names)
   10906             :     {
   10907         488 :         res.AddString(name.c_str());
   10908             :     }
   10909         317 :     return res.StealList();
   10910             : }
   10911             : 
   10912             : /************************************************************************/
   10913             : /*                          GDALGroupOpenMDArray()                      */
   10914             : /************************************************************************/
   10915             : 
   10916             : /** Open and return a multidimensional array.
   10917             :  *
   10918             :  * This is the same as the C++ method GDALGroup::OpenMDArray().
   10919             :  *
   10920             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   10921             :  */
   10922         761 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
   10923             :                                   CSLConstList papszOptions)
   10924             : {
   10925         761 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10926         761 :     VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
   10927        2283 :     auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
   10928        2283 :                                                papszOptions);
   10929         761 :     if (!array)
   10930          28 :         return nullptr;
   10931         733 :     return new GDALMDArrayHS(array);
   10932             : }
   10933             : 
   10934             : /************************************************************************/
   10935             : /*                  GDALGroupOpenMDArrayFromFullname()                  */
   10936             : /************************************************************************/
   10937             : 
   10938             : /** Open and return a multidimensional array from its fully qualified name.
   10939             :  *
   10940             :  * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
   10941             :  *
   10942             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   10943             :  *
   10944             :  * @since GDAL 3.2
   10945             :  */
   10946          16 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
   10947             :                                               const char *pszFullname,
   10948             :                                               CSLConstList papszOptions)
   10949             : {
   10950          16 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10951          16 :     VALIDATE_POINTER1(pszFullname, __func__, nullptr);
   10952          16 :     auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
   10953          48 :         std::string(pszFullname), papszOptions);
   10954          16 :     if (!array)
   10955           2 :         return nullptr;
   10956          14 :     return new GDALMDArrayHS(array);
   10957             : }
   10958             : 
   10959             : /************************************************************************/
   10960             : /*                      GDALGroupResolveMDArray()                       */
   10961             : /************************************************************************/
   10962             : 
   10963             : /** Locate an array in a group and its subgroups by name.
   10964             :  *
   10965             :  * See GDALGroup::ResolveMDArray() for description of the behavior.
   10966             :  * @since GDAL 3.2
   10967             :  */
   10968          19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
   10969             :                                      const char *pszStartingPoint,
   10970             :                                      CSLConstList papszOptions)
   10971             : {
   10972          19 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10973          19 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   10974          19 :     VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
   10975          19 :     auto array = hGroup->m_poImpl->ResolveMDArray(
   10976          57 :         std::string(pszName), std::string(pszStartingPoint), papszOptions);
   10977          19 :     if (!array)
   10978           2 :         return nullptr;
   10979          17 :     return new GDALMDArrayHS(array);
   10980             : }
   10981             : 
   10982             : /************************************************************************/
   10983             : /*                        GDALGroupGetGroupNames()                      */
   10984             : /************************************************************************/
   10985             : 
   10986             : /** Return the list of sub-groups contained in this group.
   10987             :  *
   10988             :  * This is the same as the C++ method GDALGroup::GetGroupNames().
   10989             :  *
   10990             :  * @return the group names, to be freed with CSLDestroy()
   10991             :  */
   10992          95 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
   10993             : {
   10994          95 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10995         190 :     auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
   10996         190 :     CPLStringList res;
   10997         215 :     for (const auto &name : names)
   10998             :     {
   10999         120 :         res.AddString(name.c_str());
   11000             :     }
   11001          95 :     return res.StealList();
   11002             : }
   11003             : 
   11004             : /************************************************************************/
   11005             : /*                           GDALGroupOpenGroup()                       */
   11006             : /************************************************************************/
   11007             : 
   11008             : /** Open and return a sub-group.
   11009             :  *
   11010             :  * This is the same as the C++ method GDALGroup::OpenGroup().
   11011             :  *
   11012             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11013             :  */
   11014         157 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11015             :                               CSLConstList papszOptions)
   11016             : {
   11017         157 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11018         157 :     VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
   11019             :     auto subGroup =
   11020         471 :         hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
   11021         157 :     if (!subGroup)
   11022          28 :         return nullptr;
   11023         129 :     return new GDALGroupHS(subGroup);
   11024             : }
   11025             : 
   11026             : /************************************************************************/
   11027             : /*                   GDALGroupGetVectorLayerNames()                     */
   11028             : /************************************************************************/
   11029             : 
   11030             : /** Return the list of layer names contained in this group.
   11031             :  *
   11032             :  * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
   11033             :  *
   11034             :  * @return the group names, to be freed with CSLDestroy()
   11035             :  * @since 3.4
   11036             :  */
   11037           8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
   11038             :                                     CSLConstList papszOptions)
   11039             : {
   11040           8 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11041          16 :     auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
   11042          16 :     CPLStringList res;
   11043          18 :     for (const auto &name : names)
   11044             :     {
   11045          10 :         res.AddString(name.c_str());
   11046             :     }
   11047           8 :     return res.StealList();
   11048             : }
   11049             : 
   11050             : /************************************************************************/
   11051             : /*                      GDALGroupOpenVectorLayer()                      */
   11052             : /************************************************************************/
   11053             : 
   11054             : /** Open and return a vector layer.
   11055             :  *
   11056             :  * This is the same as the C++ method GDALGroup::OpenVectorLayer().
   11057             :  *
   11058             :  * Note that the vector layer is owned by its parent GDALDatasetH, and thus
   11059             :  * the returned handled if only valid while the parent GDALDatasetH is kept
   11060             :  * opened.
   11061             :  *
   11062             :  * @return the vector layer, or nullptr.
   11063             :  * @since 3.4
   11064             :  */
   11065          12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
   11066             :                                    const char *pszVectorLayerName,
   11067             :                                    CSLConstList papszOptions)
   11068             : {
   11069          12 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11070          12 :     VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
   11071          24 :     return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
   11072          24 :         std::string(pszVectorLayerName), papszOptions));
   11073             : }
   11074             : 
   11075             : /************************************************************************/
   11076             : /*                       GDALGroupOpenMDArrayFromFullname()             */
   11077             : /************************************************************************/
   11078             : 
   11079             : /** Open and return a sub-group from its fully qualified name.
   11080             :  *
   11081             :  * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
   11082             :  *
   11083             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11084             :  *
   11085             :  * @since GDAL 3.2
   11086             :  */
   11087           3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
   11088             :                                           const char *pszFullname,
   11089             :                                           CSLConstList papszOptions)
   11090             : {
   11091           3 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11092           3 :     VALIDATE_POINTER1(pszFullname, __func__, nullptr);
   11093           3 :     auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
   11094           9 :         std::string(pszFullname), papszOptions);
   11095           3 :     if (!subGroup)
   11096           2 :         return nullptr;
   11097           1 :     return new GDALGroupHS(subGroup);
   11098             : }
   11099             : 
   11100             : /************************************************************************/
   11101             : /*                         GDALGroupGetDimensions()                     */
   11102             : /************************************************************************/
   11103             : 
   11104             : /** Return the list of dimensions contained in this group and used by its
   11105             :  * arrays.
   11106             :  *
   11107             :  * The returned array must be freed with GDALReleaseDimensions().  If only the
   11108             :  * array itself needs to be freed, CPLFree() should be called (and
   11109             :  * GDALDimensionRelease() on individual array members).
   11110             :  *
   11111             :  * This is the same as the C++ method GDALGroup::GetDimensions().
   11112             :  *
   11113             :  * @param hGroup Group.
   11114             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11115             :  * @param papszOptions Driver specific options determining how dimensions
   11116             :  * should be retrieved. Pass nullptr for default behavior.
   11117             :  *
   11118             :  * @return an array of *pnCount dimensions.
   11119             :  */
   11120          73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
   11121             :                                        CSLConstList papszOptions)
   11122             : {
   11123          73 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11124          73 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11125          73 :     auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
   11126             :     auto ret = static_cast<GDALDimensionH *>(
   11127          73 :         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
   11128         230 :     for (size_t i = 0; i < dims.size(); i++)
   11129             :     {
   11130         157 :         ret[i] = new GDALDimensionHS(dims[i]);
   11131             :     }
   11132          73 :     *pnCount = dims.size();
   11133          73 :     return ret;
   11134             : }
   11135             : 
   11136             : /************************************************************************/
   11137             : /*                          GDALGroupGetAttribute()                     */
   11138             : /************************************************************************/
   11139             : 
   11140             : /** Return an attribute by its name.
   11141             :  *
   11142             :  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
   11143             :  *
   11144             :  * The returned attribute must be freed with GDALAttributeRelease().
   11145             :  */
   11146          78 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
   11147             : {
   11148          78 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11149          78 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11150         234 :     auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
   11151          78 :     if (attr)
   11152          74 :         return new GDALAttributeHS(attr);
   11153           4 :     return nullptr;
   11154             : }
   11155             : 
   11156             : /************************************************************************/
   11157             : /*                         GDALGroupGetAttributes()                     */
   11158             : /************************************************************************/
   11159             : 
   11160             : /** Return the list of attributes contained in this group.
   11161             :  *
   11162             :  * The returned array must be freed with GDALReleaseAttributes(). If only the
   11163             :  * array itself needs to be freed, CPLFree() should be called (and
   11164             :  * GDALAttributeRelease() on individual array members).
   11165             :  *
   11166             :  * This is the same as the C++ method GDALGroup::GetAttributes().
   11167             :  *
   11168             :  * @param hGroup Group.
   11169             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11170             :  * @param papszOptions Driver specific options determining how attributes
   11171             :  * should be retrieved. Pass nullptr for default behavior.
   11172             :  *
   11173             :  * @return an array of *pnCount attributes.
   11174             :  */
   11175          67 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
   11176             :                                        CSLConstList papszOptions)
   11177             : {
   11178          67 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11179          67 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11180          67 :     auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
   11181             :     auto ret = static_cast<GDALAttributeH *>(
   11182          67 :         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
   11183         221 :     for (size_t i = 0; i < attrs.size(); i++)
   11184             :     {
   11185         154 :         ret[i] = new GDALAttributeHS(attrs[i]);
   11186             :     }
   11187          67 :     *pnCount = attrs.size();
   11188          67 :     return ret;
   11189             : }
   11190             : 
   11191             : /************************************************************************/
   11192             : /*                     GDALGroupGetStructuralInfo()                     */
   11193             : /************************************************************************/
   11194             : 
   11195             : /** Return structural information on the group.
   11196             :  *
   11197             :  * This may be the compression, etc..
   11198             :  *
   11199             :  * The return value should not be freed and is valid until GDALGroup is
   11200             :  * released or this function called again.
   11201             :  *
   11202             :  * This is the same as the C++ method GDALGroup::GetStructuralInfo().
   11203             :  */
   11204           4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
   11205             : {
   11206           4 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11207           4 :     return hGroup->m_poImpl->GetStructuralInfo();
   11208             : }
   11209             : 
   11210             : /************************************************************************/
   11211             : /*                         GDALReleaseAttributes()                      */
   11212             : /************************************************************************/
   11213             : 
   11214             : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
   11215             :  *
   11216             :  * @param attributes return pointer of above methods
   11217             :  * @param nCount *pnCount value returned by above methods
   11218             :  */
   11219         125 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
   11220             : {
   11221         408 :     for (size_t i = 0; i < nCount; i++)
   11222             :     {
   11223         283 :         delete attributes[i];
   11224             :     }
   11225         125 :     CPLFree(attributes);
   11226         125 : }
   11227             : 
   11228             : /************************************************************************/
   11229             : /*                         GDALGroupCreateGroup()                       */
   11230             : /************************************************************************/
   11231             : 
   11232             : /** Create a sub-group within a group.
   11233             :  *
   11234             :  * This is the same as the C++ method GDALGroup::CreateGroup().
   11235             :  *
   11236             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11237             :  */
   11238         173 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11239             :                                 CSLConstList papszOptions)
   11240             : {
   11241         173 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11242         173 :     VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
   11243         519 :     auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
   11244         519 :                                              papszOptions);
   11245         173 :     if (!ret)
   11246          49 :         return nullptr;
   11247         124 :     return new GDALGroupHS(ret);
   11248             : }
   11249             : 
   11250             : /************************************************************************/
   11251             : /*                         GDALGroupDeleteGroup()                       */
   11252             : /************************************************************************/
   11253             : 
   11254             : /** Delete a sub-group from a group.
   11255             :  *
   11256             :  * After this call, if a previously obtained instance of the deleted object
   11257             :  * is still alive, no method other than for freeing it should be invoked.
   11258             :  *
   11259             :  * This is the same as the C++ method GDALGroup::DeleteGroup().
   11260             :  *
   11261             :  * @return true in case of success.
   11262             :  * @since GDAL 3.8
   11263             :  */
   11264          20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11265             :                           CSLConstList papszOptions)
   11266             : {
   11267          20 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11268          20 :     VALIDATE_POINTER1(pszSubGroupName, __func__, false);
   11269          40 :     return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
   11270          20 :                                          papszOptions);
   11271             : }
   11272             : 
   11273             : /************************************************************************/
   11274             : /*                      GDALGroupCreateDimension()                      */
   11275             : /************************************************************************/
   11276             : 
   11277             : /** Create a dimension within a group.
   11278             :  *
   11279             :  * This is the same as the C++ method GDALGroup::CreateDimension().
   11280             :  *
   11281             :  * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
   11282             :  */
   11283         645 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
   11284             :                                         const char *pszType,
   11285             :                                         const char *pszDirection, GUInt64 nSize,
   11286             :                                         CSLConstList papszOptions)
   11287             : {
   11288         645 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11289         645 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11290         645 :     auto ret = hGroup->m_poImpl->CreateDimension(
   11291        1290 :         std::string(pszName), std::string(pszType ? pszType : ""),
   11292        2580 :         std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
   11293         645 :     if (!ret)
   11294           9 :         return nullptr;
   11295         636 :     return new GDALDimensionHS(ret);
   11296             : }
   11297             : 
   11298             : /************************************************************************/
   11299             : /*                      GDALGroupCreateMDArray()                        */
   11300             : /************************************************************************/
   11301             : 
   11302             : /** Create a multidimensional array within a group.
   11303             :  *
   11304             :  * This is the same as the C++ method GDALGroup::CreateMDArray().
   11305             :  *
   11306             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11307             :  */
   11308         591 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
   11309             :                                     size_t nDimensions,
   11310             :                                     GDALDimensionH *pahDimensions,
   11311             :                                     GDALExtendedDataTypeH hEDT,
   11312             :                                     CSLConstList papszOptions)
   11313             : {
   11314         591 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11315         591 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11316         591 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11317        1182 :     std::vector<std::shared_ptr<GDALDimension>> dims;
   11318         591 :     dims.reserve(nDimensions);
   11319        1389 :     for (size_t i = 0; i < nDimensions; i++)
   11320         798 :         dims.push_back(pahDimensions[i]->m_poImpl);
   11321        1773 :     auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
   11322        1773 :                                                *(hEDT->m_poImpl), papszOptions);
   11323         591 :     if (!ret)
   11324          65 :         return nullptr;
   11325         526 :     return new GDALMDArrayHS(ret);
   11326             : }
   11327             : 
   11328             : /************************************************************************/
   11329             : /*                         GDALGroupDeleteMDArray()                     */
   11330             : /************************************************************************/
   11331             : 
   11332             : /** Delete an array from a group.
   11333             :  *
   11334             :  * After this call, if a previously obtained instance of the deleted object
   11335             :  * is still alive, no method other than for freeing it should be invoked.
   11336             :  *
   11337             :  * This is the same as the C++ method GDALGroup::DeleteMDArray().
   11338             :  *
   11339             :  * @return true in case of success.
   11340             :  * @since GDAL 3.8
   11341             :  */
   11342          20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
   11343             :                             CSLConstList papszOptions)
   11344             : {
   11345          20 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11346          20 :     VALIDATE_POINTER1(pszName, __func__, false);
   11347          20 :     return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
   11348             : }
   11349             : 
   11350             : /************************************************************************/
   11351             : /*                      GDALGroupCreateAttribute()                      */
   11352             : /************************************************************************/
   11353             : 
   11354             : /** Create a attribute within a group.
   11355             :  *
   11356             :  * This is the same as the C++ method GDALGroup::CreateAttribute().
   11357             :  *
   11358             :  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
   11359             :  */
   11360         120 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
   11361             :                                         size_t nDimensions,
   11362             :                                         const GUInt64 *panDimensions,
   11363             :                                         GDALExtendedDataTypeH hEDT,
   11364             :                                         CSLConstList papszOptions)
   11365             : {
   11366         120 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11367         120 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11368         240 :     std::vector<GUInt64> dims;
   11369         120 :     dims.reserve(nDimensions);
   11370         166 :     for (size_t i = 0; i < nDimensions; i++)
   11371          46 :         dims.push_back(panDimensions[i]);
   11372         120 :     auto ret = hGroup->m_poImpl->CreateAttribute(
   11373         360 :         std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
   11374         120 :     if (!ret)
   11375          14 :         return nullptr;
   11376         106 :     return new GDALAttributeHS(ret);
   11377             : }
   11378             : 
   11379             : /************************************************************************/
   11380             : /*                         GDALGroupDeleteAttribute()                   */
   11381             : /************************************************************************/
   11382             : 
   11383             : /** Delete an attribute from a group.
   11384             :  *
   11385             :  * After this call, if a previously obtained instance of the deleted object
   11386             :  * is still alive, no method other than for freeing it should be invoked.
   11387             :  *
   11388             :  * This is the same as the C++ method GDALGroup::DeleteAttribute().
   11389             :  *
   11390             :  * @return true in case of success.
   11391             :  * @since GDAL 3.8
   11392             :  */
   11393          25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
   11394             :                               CSLConstList papszOptions)
   11395             : {
   11396          25 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11397          25 :     VALIDATE_POINTER1(pszName, __func__, false);
   11398          50 :     return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
   11399          25 :                                              papszOptions);
   11400             : }
   11401             : 
   11402             : /************************************************************************/
   11403             : /*                          GDALGroupRename()                           */
   11404             : /************************************************************************/
   11405             : 
   11406             : /** Rename the group.
   11407             :  *
   11408             :  * This is not implemented by all drivers.
   11409             :  *
   11410             :  * Drivers known to implement it: MEM, netCDF.
   11411             :  *
   11412             :  * This is the same as the C++ method GDALGroup::Rename()
   11413             :  *
   11414             :  * @return true in case of success
   11415             :  * @since GDAL 3.8
   11416             :  */
   11417          45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
   11418             : {
   11419          45 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11420          45 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   11421          45 :     return hGroup->m_poImpl->Rename(pszNewName);
   11422             : }
   11423             : 
   11424             : /************************************************************************/
   11425             : /*                 GDALGroupSubsetDimensionFromSelection()              */
   11426             : /************************************************************************/
   11427             : 
   11428             : /** Return a virtual group whose one dimension has been subset according to a
   11429             :  * selection.
   11430             :  *
   11431             :  * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
   11432             :  *
   11433             :  * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
   11434             :  */
   11435             : GDALGroupH
   11436          14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
   11437             :                                       const char *pszSelection,
   11438             :                                       CPL_UNUSED CSLConstList papszOptions)
   11439             : {
   11440          14 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11441          14 :     VALIDATE_POINTER1(pszSelection, __func__, nullptr);
   11442          14 :     auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
   11443          42 :         std::string(pszSelection));
   11444          14 :     if (!hNewGroup)
   11445           8 :         return nullptr;
   11446           6 :     return new GDALGroupHS(hNewGroup);
   11447             : }
   11448             : 
   11449             : /************************************************************************/
   11450             : /*                        GDALMDArrayRelease()                          */
   11451             : /************************************************************************/
   11452             : 
   11453             : /** Release the GDAL in-memory object associated with a GDALMDArray.
   11454             :  *
   11455             :  * Note: when applied on a object coming from a driver, this does not
   11456             :  * destroy the object in the file, database, etc...
   11457             :  */
   11458        1960 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
   11459             : {
   11460        1960 :     delete hMDArray;
   11461        1960 : }
   11462             : 
   11463             : /************************************************************************/
   11464             : /*                        GDALMDArrayGetName()                          */
   11465             : /************************************************************************/
   11466             : 
   11467             : /** Return array name.
   11468             :  *
   11469             :  * This is the same as the C++ method GDALMDArray::GetName()
   11470             :  */
   11471          83 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
   11472             : {
   11473          83 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11474          83 :     return hArray->m_poImpl->GetName().c_str();
   11475             : }
   11476             : 
   11477             : /************************************************************************/
   11478             : /*                    GDALMDArrayGetFullName()                          */
   11479             : /************************************************************************/
   11480             : 
   11481             : /** Return array full name.
   11482             :  *
   11483             :  * This is the same as the C++ method GDALMDArray::GetFullName()
   11484             :  */
   11485          50 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
   11486             : {
   11487          50 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11488          50 :     return hArray->m_poImpl->GetFullName().c_str();
   11489             : }
   11490             : 
   11491             : /************************************************************************/
   11492             : /*                        GDALMDArrayGetName()                          */
   11493             : /************************************************************************/
   11494             : 
   11495             : /** Return the total number of values in the array.
   11496             :  *
   11497             :  * This is the same as the C++ method
   11498             :  * GDALAbstractMDArray::GetTotalElementsCount()
   11499             :  */
   11500           6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
   11501             : {
   11502           6 :     VALIDATE_POINTER1(hArray, __func__, 0);
   11503           6 :     return hArray->m_poImpl->GetTotalElementsCount();
   11504             : }
   11505             : 
   11506             : /************************************************************************/
   11507             : /*                        GDALMDArrayGetDimensionCount()                */
   11508             : /************************************************************************/
   11509             : 
   11510             : /** Return the number of dimensions.
   11511             :  *
   11512             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
   11513             :  */
   11514       10104 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
   11515             : {
   11516       10104 :     VALIDATE_POINTER1(hArray, __func__, 0);
   11517       10104 :     return hArray->m_poImpl->GetDimensionCount();
   11518             : }
   11519             : 
   11520             : /************************************************************************/
   11521             : /*                        GDALMDArrayGetDimensions()                    */
   11522             : /************************************************************************/
   11523             : 
   11524             : /** Return the dimensions of the array
   11525             :  *
   11526             :  * The returned array must be freed with GDALReleaseDimensions(). If only the
   11527             :  * array itself needs to be freed, CPLFree() should be called (and
   11528             :  * GDALDimensionRelease() on individual array members).
   11529             :  *
   11530             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
   11531             :  *
   11532             :  * @param hArray Array.
   11533             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11534             :  *
   11535             :  * @return an array of *pnCount dimensions.
   11536             :  */
   11537        2226 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
   11538             : {
   11539        2226 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11540        2226 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11541        2226 :     const auto &dims(hArray->m_poImpl->GetDimensions());
   11542             :     auto ret = static_cast<GDALDimensionH *>(
   11543        2226 :         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
   11544        6269 :     for (size_t i = 0; i < dims.size(); i++)
   11545             :     {
   11546        4043 :         ret[i] = new GDALDimensionHS(dims[i]);
   11547             :     }
   11548        2226 :     *pnCount = dims.size();
   11549        2226 :     return ret;
   11550             : }
   11551             : 
   11552             : /************************************************************************/
   11553             : /*                        GDALReleaseDimensions()                       */
   11554             : /************************************************************************/
   11555             : 
   11556             : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
   11557             :  *
   11558             :  * @param dims return pointer of above methods
   11559             :  * @param nCount *pnCount value returned by above methods
   11560             :  */
   11561        2299 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
   11562             : {
   11563        6499 :     for (size_t i = 0; i < nCount; i++)
   11564             :     {
   11565        4200 :         delete dims[i];
   11566             :     }
   11567        2299 :     CPLFree(dims);
   11568        2299 : }
   11569             : 
   11570             : /************************************************************************/
   11571             : /*                        GDALMDArrayGetDataType()                     */
   11572             : /************************************************************************/
   11573             : 
   11574             : /** Return the data type
   11575             :  *
   11576             :  * The return must be freed with GDALExtendedDataTypeRelease().
   11577             :  */
   11578        3816 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
   11579             : {
   11580        3816 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11581             :     return new GDALExtendedDataTypeHS(
   11582        3816 :         new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
   11583             : }
   11584             : 
   11585             : /************************************************************************/
   11586             : /*                          GDALMDArrayRead()                           */
   11587             : /************************************************************************/
   11588             : 
   11589             : /** Read part or totality of a multidimensional array.
   11590             :  *
   11591             :  * This is the same as the C++ method GDALAbstractMDArray::Read()
   11592             :  *
   11593             :  * @return TRUE in case of success.
   11594             :  */
   11595        1898 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   11596             :                     const size_t *count, const GInt64 *arrayStep,
   11597             :                     const GPtrDiff_t *bufferStride,
   11598             :                     GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
   11599             :                     const void *pDstBufferAllocStart,
   11600             :                     size_t nDstBufferAllocSize)
   11601             : {
   11602        1898 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11603        1898 :     if ((arrayStartIdx == nullptr || count == nullptr) &&
   11604           0 :         hArray->m_poImpl->GetDimensionCount() > 0)
   11605             :     {
   11606           0 :         VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
   11607           0 :         VALIDATE_POINTER1(count, __func__, FALSE);
   11608             :     }
   11609        1898 :     VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
   11610        1898 :     VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
   11611             :     // coverity[var_deref_model]
   11612        3796 :     return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
   11613        1898 :                                   *(bufferDataType->m_poImpl), pDstBuffer,
   11614        1898 :                                   pDstBufferAllocStart, nDstBufferAllocSize);
   11615             : }
   11616             : 
   11617             : /************************************************************************/
   11618             : /*                          GDALMDArrayWrite()                           */
   11619             : /************************************************************************/
   11620             : 
   11621             : /** Write part or totality of a multidimensional array.
   11622             :  *
   11623             :  * This is the same as the C++ method GDALAbstractMDArray::Write()
   11624             :  *
   11625             :  * @return TRUE in case of success.
   11626             :  */
   11627         533 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   11628             :                      const size_t *count, const GInt64 *arrayStep,
   11629             :                      const GPtrDiff_t *bufferStride,
   11630             :                      GDALExtendedDataTypeH bufferDataType,
   11631             :                      const void *pSrcBuffer, const void *pSrcBufferAllocStart,
   11632             :                      size_t nSrcBufferAllocSize)
   11633             : {
   11634         533 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11635         533 :     if ((arrayStartIdx == nullptr || count == nullptr) &&
   11636           0 :         hArray->m_poImpl->GetDimensionCount() > 0)
   11637             :     {
   11638           0 :         VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
   11639           0 :         VALIDATE_POINTER1(count, __func__, FALSE);
   11640             :     }
   11641         533 :     VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
   11642         533 :     VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
   11643             :     // coverity[var_deref_model]
   11644        1066 :     return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
   11645         533 :                                    bufferStride, *(bufferDataType->m_poImpl),
   11646             :                                    pSrcBuffer, pSrcBufferAllocStart,
   11647         533 :                                    nSrcBufferAllocSize);
   11648             : }
   11649             : 
   11650             : /************************************************************************/
   11651             : /*                       GDALMDArrayAdviseRead()                        */
   11652             : /************************************************************************/
   11653             : 
   11654             : /** Advise driver of upcoming read requests.
   11655             :  *
   11656             :  * This is the same as the C++ method GDALMDArray::AdviseRead()
   11657             :  *
   11658             :  * @return TRUE in case of success.
   11659             :  *
   11660             :  * @since GDAL 3.2
   11661             :  */
   11662           0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   11663             :                           const size_t *count)
   11664             : {
   11665           0 :     return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
   11666             : }
   11667             : 
   11668             : /************************************************************************/
   11669             : /*                      GDALMDArrayAdviseReadEx()                       */
   11670             : /************************************************************************/
   11671             : 
   11672             : /** Advise driver of upcoming read requests.
   11673             :  *
   11674             :  * This is the same as the C++ method GDALMDArray::AdviseRead()
   11675             :  *
   11676             :  * @return TRUE in case of success.
   11677             :  *
   11678             :  * @since GDAL 3.4
   11679             :  */
   11680          22 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   11681             :                             const size_t *count, CSLConstList papszOptions)
   11682             : {
   11683          22 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11684             :     // coverity[var_deref_model]
   11685          22 :     return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
   11686             : }
   11687             : 
   11688             : /************************************************************************/
   11689             : /*                         GDALMDArrayGetAttribute()                    */
   11690             : /************************************************************************/
   11691             : 
   11692             : /** Return an attribute by its name.
   11693             :  *
   11694             :  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
   11695             :  *
   11696             :  * The returned attribute must be freed with GDALAttributeRelease().
   11697             :  */
   11698         119 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
   11699             : {
   11700         119 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11701         119 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11702         357 :     auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
   11703         119 :     if (attr)
   11704         110 :         return new GDALAttributeHS(attr);
   11705           9 :     return nullptr;
   11706             : }
   11707             : 
   11708             : /************************************************************************/
   11709             : /*                        GDALMDArrayGetAttributes()                    */
   11710             : /************************************************************************/
   11711             : 
   11712             : /** Return the list of attributes contained in this array.
   11713             :  *
   11714             :  * The returned array must be freed with GDALReleaseAttributes(). If only the
   11715             :  * array itself needs to be freed, CPLFree() should be called (and
   11716             :  * GDALAttributeRelease() on individual array members).
   11717             :  *
   11718             :  * This is the same as the C++ method GDALMDArray::GetAttributes().
   11719             :  *
   11720             :  * @param hArray Array.
   11721             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11722             :  * @param papszOptions Driver specific options determining how attributes
   11723             :  * should be retrieved. Pass nullptr for default behavior.
   11724             :  *
   11725             :  * @return an array of *pnCount attributes.
   11726             :  */
   11727          58 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
   11728             :                                          CSLConstList papszOptions)
   11729             : {
   11730          58 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11731          58 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11732          58 :     auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
   11733             :     auto ret = static_cast<GDALAttributeH *>(
   11734          58 :         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
   11735         187 :     for (size_t i = 0; i < attrs.size(); i++)
   11736             :     {
   11737         129 :         ret[i] = new GDALAttributeHS(attrs[i]);
   11738             :     }
   11739          58 :     *pnCount = attrs.size();
   11740          58 :     return ret;
   11741             : }
   11742             : 
   11743             : /************************************************************************/
   11744             : /*                       GDALMDArrayCreateAttribute()                   */
   11745             : /************************************************************************/
   11746             : 
   11747             : /** Create a attribute within an array.
   11748             :  *
   11749             :  * This is the same as the C++ method GDALMDArray::CreateAttribute().
   11750             :  *
   11751             :  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
   11752             :  */
   11753         149 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
   11754             :                                           const char *pszName,
   11755             :                                           size_t nDimensions,
   11756             :                                           const GUInt64 *panDimensions,
   11757             :                                           GDALExtendedDataTypeH hEDT,
   11758             :                                           CSLConstList papszOptions)
   11759             : {
   11760         149 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11761         149 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11762         149 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11763         298 :     std::vector<GUInt64> dims;
   11764         149 :     dims.reserve(nDimensions);
   11765         174 :     for (size_t i = 0; i < nDimensions; i++)
   11766          25 :         dims.push_back(panDimensions[i]);
   11767         149 :     auto ret = hArray->m_poImpl->CreateAttribute(
   11768         447 :         std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
   11769         149 :     if (!ret)
   11770           9 :         return nullptr;
   11771         140 :     return new GDALAttributeHS(ret);
   11772             : }
   11773             : 
   11774             : /************************************************************************/
   11775             : /*                       GDALMDArrayDeleteAttribute()                   */
   11776             : /************************************************************************/
   11777             : 
   11778             : /** Delete an attribute from an array.
   11779             :  *
   11780             :  * After this call, if a previously obtained instance of the deleted object
   11781             :  * is still alive, no method other than for freeing it should be invoked.
   11782             :  *
   11783             :  * This is the same as the C++ method GDALMDArray::DeleteAttribute().
   11784             :  *
   11785             :  * @return true in case of success.
   11786             :  * @since GDAL 3.8
   11787             :  */
   11788          24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
   11789             :                                 CSLConstList papszOptions)
   11790             : {
   11791          24 :     VALIDATE_POINTER1(hArray, __func__, false);
   11792          24 :     VALIDATE_POINTER1(pszName, __func__, false);
   11793          48 :     return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
   11794          24 :                                              papszOptions);
   11795             : }
   11796             : 
   11797             : /************************************************************************/
   11798             : /*                       GDALMDArrayGetRawNoDataValue()                 */
   11799             : /************************************************************************/
   11800             : 
   11801             : /** Return the nodata value as a "raw" value.
   11802             :  *
   11803             :  * The value returned might be nullptr in case of no nodata value. When
   11804             :  * a nodata value is registered, a non-nullptr will be returned whose size in
   11805             :  * bytes is GetDataType().GetSize().
   11806             :  *
   11807             :  * The returned value should not be modified or freed.
   11808             :  *
   11809             :  * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
   11810             :  *
   11811             :  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
   11812             :  */
   11813          74 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
   11814             : {
   11815          74 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11816          74 :     return hArray->m_poImpl->GetRawNoDataValue();
   11817             : }
   11818             : 
   11819             : /************************************************************************/
   11820             : /*                      GDALMDArrayGetNoDataValueAsDouble()             */
   11821             : /************************************************************************/
   11822             : 
   11823             : /** Return the nodata value as a double.
   11824             :  *
   11825             :  * The value returned might be nullptr in case of no nodata value. When
   11826             :  * a nodata value is registered, a non-nullptr will be returned whose size in
   11827             :  * bytes is GetDataType().GetSize().
   11828             :  *
   11829             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
   11830             :  *
   11831             :  * @param hArray Array handle.
   11832             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   11833             :  * if a nodata value exists and can be converted to double. Might be nullptr.
   11834             :  *
   11835             :  * @return the nodata value as a double. A 0.0 value might also indicate the
   11836             :  * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
   11837             :  * will be set to false then).
   11838             :  */
   11839         118 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
   11840             :                                          int *pbHasNoDataValue)
   11841             : {
   11842         118 :     VALIDATE_POINTER1(hArray, __func__, 0);
   11843         118 :     bool bHasNodataValue = false;
   11844         118 :     double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
   11845         118 :     if (pbHasNoDataValue)
   11846         118 :         *pbHasNoDataValue = bHasNodataValue;
   11847         118 :     return ret;
   11848             : }
   11849             : 
   11850             : /************************************************************************/
   11851             : /*                      GDALMDArrayGetNoDataValueAsInt64()              */
   11852             : /************************************************************************/
   11853             : 
   11854             : /** Return the nodata value as a Int64.
   11855             :  *
   11856             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
   11857             :  *
   11858             :  * @param hArray Array handle.
   11859             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   11860             :  * if a nodata value exists and can be converted to Int64. Might be nullptr.
   11861             :  *
   11862             :  * @return the nodata value as a Int64.
   11863             :  * @since GDAL 3.5
   11864             :  */
   11865          11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
   11866             :                                          int *pbHasNoDataValue)
   11867             : {
   11868          11 :     VALIDATE_POINTER1(hArray, __func__, 0);
   11869          11 :     bool bHasNodataValue = false;
   11870          11 :     const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
   11871          11 :     if (pbHasNoDataValue)
   11872          11 :         *pbHasNoDataValue = bHasNodataValue;
   11873          11 :     return ret;
   11874             : }
   11875             : 
   11876             : /************************************************************************/
   11877             : /*                      GDALMDArrayGetNoDataValueAsUInt64()              */
   11878             : /************************************************************************/
   11879             : 
   11880             : /** Return the nodata value as a UInt64.
   11881             :  *
   11882             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
   11883             :  *
   11884             :  * @param hArray Array handle.
   11885             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   11886             :  * if a nodata value exists and can be converted to UInt64. Might be nullptr.
   11887             :  *
   11888             :  * @return the nodata value as a UInt64.
   11889             :  * @since GDAL 3.5
   11890             :  */
   11891           7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
   11892             :                                            int *pbHasNoDataValue)
   11893             : {
   11894           7 :     VALIDATE_POINTER1(hArray, __func__, 0);
   11895           7 :     bool bHasNodataValue = false;
   11896           7 :     const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
   11897           7 :     if (pbHasNoDataValue)
   11898           7 :         *pbHasNoDataValue = bHasNodataValue;
   11899           7 :     return ret;
   11900             : }
   11901             : 
   11902             : /************************************************************************/
   11903             : /*                     GDALMDArraySetRawNoDataValue()                   */
   11904             : /************************************************************************/
   11905             : 
   11906             : /** Set the nodata value as a "raw" value.
   11907             :  *
   11908             :  * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
   11909             :  * void*).
   11910             :  *
   11911             :  * @return TRUE in case of success.
   11912             :  */
   11913          14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
   11914             : {
   11915          14 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11916          14 :     return hArray->m_poImpl->SetRawNoDataValue(pNoData);
   11917             : }
   11918             : 
   11919             : /************************************************************************/
   11920             : /*                   GDALMDArraySetNoDataValueAsDouble()                */
   11921             : /************************************************************************/
   11922             : 
   11923             : /** Set the nodata value as a double.
   11924             :  *
   11925             :  * If the natural data type of the attribute/array is not double, type
   11926             :  * conversion will occur to the type returned by GetDataType().
   11927             :  *
   11928             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
   11929             :  *
   11930             :  * @return TRUE in case of success.
   11931             :  */
   11932          51 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
   11933             : {
   11934          51 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11935          51 :     return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
   11936             : }
   11937             : 
   11938             : /************************************************************************/
   11939             : /*                   GDALMDArraySetNoDataValueAsInt64()                 */
   11940             : /************************************************************************/
   11941             : 
   11942             : /** Set the nodata value as a Int64.
   11943             :  *
   11944             :  * If the natural data type of the attribute/array is not Int64, type conversion
   11945             :  * will occur to the type returned by GetDataType().
   11946             :  *
   11947             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
   11948             :  *
   11949             :  * @return TRUE in case of success.
   11950             :  * @since GDAL 3.5
   11951             :  */
   11952           1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
   11953             : {
   11954           1 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11955           1 :     return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
   11956             : }
   11957             : 
   11958             : /************************************************************************/
   11959             : /*                   GDALMDArraySetNoDataValueAsUInt64()                */
   11960             : /************************************************************************/
   11961             : 
   11962             : /** Set the nodata value as a UInt64.
   11963             :  *
   11964             :  * If the natural data type of the attribute/array is not UInt64, type
   11965             :  * conversion will occur to the type returned by GetDataType().
   11966             :  *
   11967             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
   11968             :  *
   11969             :  * @return TRUE in case of success.
   11970             :  * @since GDAL 3.5
   11971             :  */
   11972           1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
   11973             :                                       uint64_t nNoDataValue)
   11974             : {
   11975           1 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11976           1 :     return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
   11977             : }
   11978             : 
   11979             : /************************************************************************/
   11980             : /*                        GDALMDArrayResize()                           */
   11981             : /************************************************************************/
   11982             : 
   11983             : /** Resize an array to new dimensions.
   11984             :  *
   11985             :  * Not all drivers may allow this operation, and with restrictions (e.g.
   11986             :  * for netCDF, this is limited to growing of "unlimited" dimensions)
   11987             :  *
   11988             :  * Resizing a dimension used in other arrays will cause those other arrays
   11989             :  * to be resized.
   11990             :  *
   11991             :  * This is the same as the C++ method GDALMDArray::Resize().
   11992             :  *
   11993             :  * @param hArray Array.
   11994             :  * @param panNewDimSizes Array of GetDimensionCount() values containing the
   11995             :  *                       new size of each indexing dimension.
   11996             :  * @param papszOptions Options. (Driver specific)
   11997             :  * @return true in case of success.
   11998             :  * @since GDAL 3.7
   11999             :  */
   12000          42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
   12001             :                        CSLConstList papszOptions)
   12002             : {
   12003          42 :     VALIDATE_POINTER1(hArray, __func__, false);
   12004          42 :     VALIDATE_POINTER1(panNewDimSizes, __func__, false);
   12005          84 :     std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
   12006         125 :     for (size_t i = 0; i < anNewDimSizes.size(); ++i)
   12007             :     {
   12008          83 :         anNewDimSizes[i] = panNewDimSizes[i];
   12009             :     }
   12010          42 :     return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
   12011             : }
   12012             : 
   12013             : /************************************************************************/
   12014             : /*                          GDALMDArraySetScale()                       */
   12015             : /************************************************************************/
   12016             : 
   12017             : /** Set the scale value to apply to raw values.
   12018             :  *
   12019             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12020             :  *
   12021             :  * This is the same as the C++ method GDALMDArray::SetScale().
   12022             :  *
   12023             :  * @return TRUE in case of success.
   12024             :  */
   12025           0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
   12026             : {
   12027           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12028           0 :     return hArray->m_poImpl->SetScale(dfScale);
   12029             : }
   12030             : 
   12031             : /************************************************************************/
   12032             : /*                        GDALMDArraySetScaleEx()                       */
   12033             : /************************************************************************/
   12034             : 
   12035             : /** Set the scale value to apply to raw values.
   12036             :  *
   12037             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12038             :  *
   12039             :  * This is the same as the C++ method GDALMDArray::SetScale().
   12040             :  *
   12041             :  * @return TRUE in case of success.
   12042             :  * @since GDAL 3.3
   12043             :  */
   12044          21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
   12045             :                           GDALDataType eStorageType)
   12046             : {
   12047          21 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12048          21 :     return hArray->m_poImpl->SetScale(dfScale, eStorageType);
   12049             : }
   12050             : 
   12051             : /************************************************************************/
   12052             : /*                          GDALMDArraySetOffset()                       */
   12053             : /************************************************************************/
   12054             : 
   12055             : /** Set the scale value to apply to raw values.
   12056             :  *
   12057             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12058             :  *
   12059             :  * This is the same as the C++ method GDALMDArray::SetOffset().
   12060             :  *
   12061             :  * @return TRUE in case of success.
   12062             :  */
   12063           0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
   12064             : {
   12065           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12066           0 :     return hArray->m_poImpl->SetOffset(dfOffset);
   12067             : }
   12068             : 
   12069             : /************************************************************************/
   12070             : /*                       GDALMDArraySetOffsetEx()                       */
   12071             : /************************************************************************/
   12072             : 
   12073             : /** Set the scale value to apply to raw values.
   12074             :  *
   12075             :  * unscaled_value = raw_value * GetOffset() + GetOffset()
   12076             :  *
   12077             :  * This is the same as the C++ method GDALMDArray::SetOffset().
   12078             :  *
   12079             :  * @return TRUE in case of success.
   12080             :  * @since GDAL 3.3
   12081             :  */
   12082          21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
   12083             :                            GDALDataType eStorageType)
   12084             : {
   12085          21 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12086          21 :     return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
   12087             : }
   12088             : 
   12089             : /************************************************************************/
   12090             : /*                          GDALMDArrayGetScale()                       */
   12091             : /************************************************************************/
   12092             : 
   12093             : /** Get the scale value to apply to raw values.
   12094             :  *
   12095             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12096             :  *
   12097             :  * This is the same as the C++ method GDALMDArray::GetScale().
   12098             :  *
   12099             :  * @return the scale value
   12100             :  */
   12101         103 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
   12102             : {
   12103         103 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12104         103 :     bool bHasValue = false;
   12105         103 :     double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
   12106         103 :     if (pbHasValue)
   12107         103 :         *pbHasValue = bHasValue;
   12108         103 :     return dfRet;
   12109             : }
   12110             : 
   12111             : /************************************************************************/
   12112             : /*                        GDALMDArrayGetScaleEx()                       */
   12113             : /************************************************************************/
   12114             : 
   12115             : /** Get the scale value to apply to raw values.
   12116             :  *
   12117             :  * unscaled_value = raw_value * GetScale() + GetScale()
   12118             :  *
   12119             :  * This is the same as the C++ method GDALMDArray::GetScale().
   12120             :  *
   12121             :  * @return the scale value
   12122             :  * @since GDAL 3.3
   12123             :  */
   12124           5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
   12125             :                              GDALDataType *peStorageType)
   12126             : {
   12127           5 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12128           5 :     bool bHasValue = false;
   12129           5 :     double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
   12130           5 :     if (pbHasValue)
   12131           5 :         *pbHasValue = bHasValue;
   12132           5 :     return dfRet;
   12133             : }
   12134             : 
   12135             : /************************************************************************/
   12136             : /*                          GDALMDArrayGetOffset()                      */
   12137             : /************************************************************************/
   12138             : 
   12139             : /** Get the scale value to apply to raw values.
   12140             :  *
   12141             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12142             :  *
   12143             :  * This is the same as the C++ method GDALMDArray::GetOffset().
   12144             :  *
   12145             :  * @return the scale value
   12146             :  */
   12147         100 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
   12148             : {
   12149         100 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12150         100 :     bool bHasValue = false;
   12151         100 :     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
   12152         100 :     if (pbHasValue)
   12153         100 :         *pbHasValue = bHasValue;
   12154         100 :     return dfRet;
   12155             : }
   12156             : 
   12157             : /************************************************************************/
   12158             : /*                        GDALMDArrayGetOffsetEx()                      */
   12159             : /************************************************************************/
   12160             : 
   12161             : /** Get the scale value to apply to raw values.
   12162             :  *
   12163             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12164             :  *
   12165             :  * This is the same as the C++ method GDALMDArray::GetOffset().
   12166             :  *
   12167             :  * @return the scale value
   12168             :  * @since GDAL 3.3
   12169             :  */
   12170           5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
   12171             :                               GDALDataType *peStorageType)
   12172             : {
   12173           5 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12174           5 :     bool bHasValue = false;
   12175           5 :     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
   12176           5 :     if (pbHasValue)
   12177           5 :         *pbHasValue = bHasValue;
   12178           5 :     return dfRet;
   12179             : }
   12180             : 
   12181             : /************************************************************************/
   12182             : /*                      GDALMDArrayGetBlockSize()                       */
   12183             : /************************************************************************/
   12184             : 
   12185             : /** Return the "natural" block size of the array along all dimensions.
   12186             :  *
   12187             :  * Some drivers might organize the array in tiles/blocks and reading/writing
   12188             :  * aligned on those tile/block boundaries will be more efficient.
   12189             :  *
   12190             :  * The returned number of elements in the vector is the same as
   12191             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
   12192             :  * the natural block size along the considered dimension.
   12193             :  * "Flat" arrays will typically return a vector of values set to 0.
   12194             :  *
   12195             :  * The default implementation will return a vector of values set to 0.
   12196             :  *
   12197             :  * This method is used by GetProcessingChunkSize().
   12198             :  *
   12199             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
   12200             :  * theoretical case of a 32-bit platform, this might exceed its size_t
   12201             :  * allocation capabilities.
   12202             :  *
   12203             :  * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
   12204             :  *
   12205             :  * @return the block size, in number of elements along each dimension.
   12206             :  */
   12207          93 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
   12208             : {
   12209          93 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12210          93 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12211          93 :     auto res = hArray->m_poImpl->GetBlockSize();
   12212          93 :     auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
   12213         285 :     for (size_t i = 0; i < res.size(); i++)
   12214             :     {
   12215         192 :         ret[i] = res[i];
   12216             :     }
   12217          93 :     *pnCount = res.size();
   12218          93 :     return ret;
   12219             : }
   12220             : 
   12221             : /***********************************************************************/
   12222             : /*                   GDALMDArrayGetProcessingChunkSize()               */
   12223             : /************************************************************************/
   12224             : 
   12225             : /** \brief Return an optimal chunk size for read/write operations, given the
   12226             :  * natural block size and memory constraints specified.
   12227             :  *
   12228             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
   12229             :  * multiple of those returned by GetBlockSize() (unless the block define by
   12230             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
   12231             :  * returned by this method).
   12232             :  *
   12233             :  * This is the same as the C++ method
   12234             :  * GDALAbstractMDArray::GetProcessingChunkSize().
   12235             :  *
   12236             :  * @param hArray Array.
   12237             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12238             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
   12239             :  * chunk.
   12240             :  *
   12241             :  * @return the chunk size, in number of elements along each dimension.
   12242             :  */
   12243             : 
   12244           1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
   12245             :                                           size_t nMaxChunkMemory)
   12246             : {
   12247           1 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12248           1 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12249           1 :     auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
   12250           1 :     auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
   12251           3 :     for (size_t i = 0; i < res.size(); i++)
   12252             :     {
   12253           2 :         ret[i] = res[i];
   12254             :     }
   12255           1 :     *pnCount = res.size();
   12256           1 :     return ret;
   12257             : }
   12258             : 
   12259             : /************************************************************************/
   12260             : /*                     GDALMDArrayGetStructuralInfo()                   */
   12261             : /************************************************************************/
   12262             : 
   12263             : /** Return structural information on the array.
   12264             :  *
   12265             :  * This may be the compression, etc..
   12266             :  *
   12267             :  * The return value should not be freed and is valid until GDALMDArray is
   12268             :  * released or this function called again.
   12269             :  *
   12270             :  * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
   12271             :  */
   12272           6 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
   12273             : {
   12274           6 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12275           6 :     return hArray->m_poImpl->GetStructuralInfo();
   12276             : }
   12277             : 
   12278             : /************************************************************************/
   12279             : /*                        GDALMDArrayGetView()                          */
   12280             : /************************************************************************/
   12281             : 
   12282             : /** Return a view of the array using slicing or field access.
   12283             :  *
   12284             :  * The returned object should be released with GDALMDArrayRelease().
   12285             :  *
   12286             :  * This is the same as the C++ method GDALMDArray::GetView().
   12287             :  */
   12288         430 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
   12289             : {
   12290         430 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12291         430 :     VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
   12292        1290 :     auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
   12293         430 :     if (!sliced)
   12294          22 :         return nullptr;
   12295         408 :     return new GDALMDArrayHS(sliced);
   12296             : }
   12297             : 
   12298             : /************************************************************************/
   12299             : /*                       GDALMDArrayTranspose()                         */
   12300             : /************************************************************************/
   12301             : 
   12302             : /** Return a view of the array whose axis have been reordered.
   12303             :  *
   12304             :  * The returned object should be released with GDALMDArrayRelease().
   12305             :  *
   12306             :  * This is the same as the C++ method GDALMDArray::Transpose().
   12307             :  */
   12308          44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
   12309             :                                   const int *panMapNewAxisToOldAxis)
   12310             : {
   12311          44 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12312          88 :     std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
   12313          44 :     if (nNewAxisCount)
   12314             :     {
   12315          43 :         memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
   12316             :                nNewAxisCount * sizeof(int));
   12317             :     }
   12318          88 :     auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
   12319          44 :     if (!reordered)
   12320           7 :         return nullptr;
   12321          37 :     return new GDALMDArrayHS(reordered);
   12322             : }
   12323             : 
   12324             : /************************************************************************/
   12325             : /*                      GDALMDArrayGetUnscaled()                        */
   12326             : /************************************************************************/
   12327             : 
   12328             : /** Return an array that is the unscaled version of the current one.
   12329             :  *
   12330             :  * That is each value of the unscaled array will be
   12331             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12332             :  *
   12333             :  * Starting with GDAL 3.3, the Write() method is implemented and will convert
   12334             :  * from unscaled values to raw values.
   12335             :  *
   12336             :  * The returned object should be released with GDALMDArrayRelease().
   12337             :  *
   12338             :  * This is the same as the C++ method GDALMDArray::GetUnscaled().
   12339             :  */
   12340          13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
   12341             : {
   12342          13 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12343          26 :     auto unscaled = hArray->m_poImpl->GetUnscaled();
   12344          13 :     if (!unscaled)
   12345           0 :         return nullptr;
   12346          13 :     return new GDALMDArrayHS(unscaled);
   12347             : }
   12348             : 
   12349             : /************************************************************************/
   12350             : /*                          GDALMDArrayGetMask()                         */
   12351             : /************************************************************************/
   12352             : 
   12353             : /** Return an array that is a mask for the current array
   12354             :  *
   12355             :  * This array will be of type Byte, with values set to 0 to indicate invalid
   12356             :  * pixels of the current array, and values set to 1 to indicate valid pixels.
   12357             :  *
   12358             :  * The returned object should be released with GDALMDArrayRelease().
   12359             :  *
   12360             :  * This is the same as the C++ method GDALMDArray::GetMask().
   12361             :  */
   12362          35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
   12363             : {
   12364          35 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12365          70 :     auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
   12366          35 :     if (!unscaled)
   12367           7 :         return nullptr;
   12368          28 :     return new GDALMDArrayHS(unscaled);
   12369             : }
   12370             : 
   12371             : /************************************************************************/
   12372             : /*                   GDALMDArrayGetResampled()                          */
   12373             : /************************************************************************/
   12374             : 
   12375             : /** Return an array that is a resampled / reprojected view of the current array
   12376             :  *
   12377             :  * This is the same as the C++ method GDALMDArray::GetResampled().
   12378             :  *
   12379             :  * Currently this method can only resample along the last 2 dimensions, unless
   12380             :  * orthorectifying a NASA EMIT dataset.
   12381             :  *
   12382             :  * The returned object should be released with GDALMDArrayRelease().
   12383             :  *
   12384             :  * @since 3.4
   12385             :  */
   12386          34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
   12387             :                                      const GDALDimensionH *pahNewDims,
   12388             :                                      GDALRIOResampleAlg resampleAlg,
   12389             :                                      OGRSpatialReferenceH hTargetSRS,
   12390             :                                      CSLConstList papszOptions)
   12391             : {
   12392          34 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12393          34 :     VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
   12394          68 :     std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
   12395         112 :     for (size_t i = 0; i < nNewDimCount; ++i)
   12396             :     {
   12397          78 :         if (pahNewDims[i])
   12398           8 :             apoNewDims[i] = pahNewDims[i]->m_poImpl;
   12399             :     }
   12400          34 :     auto poNewArray = hArray->m_poImpl->GetResampled(
   12401          34 :         apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
   12402          68 :         papszOptions);
   12403          34 :     if (!poNewArray)
   12404           8 :         return nullptr;
   12405          26 :     return new GDALMDArrayHS(poNewArray);
   12406             : }
   12407             : 
   12408             : /************************************************************************/
   12409             : /*                      GDALMDArraySetUnit()                            */
   12410             : /************************************************************************/
   12411             : 
   12412             : /** Set the variable unit.
   12413             :  *
   12414             :  * Values should conform as much as possible with those allowed by
   12415             :  * the NetCDF CF conventions:
   12416             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
   12417             :  * but others might be returned.
   12418             :  *
   12419             :  * Few examples are "meter", "degrees", "second", ...
   12420             :  * Empty value means unknown.
   12421             :  *
   12422             :  * This is the same as the C function GDALMDArraySetUnit()
   12423             :  *
   12424             :  * @param hArray array.
   12425             :  * @param pszUnit unit name.
   12426             :  * @return TRUE in case of success.
   12427             :  */
   12428          15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
   12429             : {
   12430          15 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12431          15 :     return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
   12432             : }
   12433             : 
   12434             : /************************************************************************/
   12435             : /*                      GDALMDArrayGetUnit()                            */
   12436             : /************************************************************************/
   12437             : 
   12438             : /** Return the array unit.
   12439             :  *
   12440             :  * Values should conform as much as possible with those allowed by
   12441             :  * the NetCDF CF conventions:
   12442             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
   12443             :  * but others might be returned.
   12444             :  *
   12445             :  * Few examples are "meter", "degrees", "second", ...
   12446             :  * Empty value means unknown.
   12447             :  *
   12448             :  * The return value should not be freed and is valid until GDALMDArray is
   12449             :  * released or this function called again.
   12450             :  *
   12451             :  * This is the same as the C++ method GDALMDArray::GetUnit().
   12452             :  */
   12453         111 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
   12454             : {
   12455         111 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12456         111 :     return hArray->m_poImpl->GetUnit().c_str();
   12457             : }
   12458             : 
   12459             : /************************************************************************/
   12460             : /*                      GDALMDArrayGetSpatialRef()                      */
   12461             : /************************************************************************/
   12462             : 
   12463             : /** Assign a spatial reference system object to the array.
   12464             :  *
   12465             :  * This is the same as the C++ method GDALMDArray::SetSpatialRef().
   12466             :  * @return TRUE in case of success.
   12467             :  */
   12468          30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
   12469             : {
   12470          30 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12471          60 :     return hArray->m_poImpl->SetSpatialRef(
   12472          60 :         OGRSpatialReference::FromHandle(hSRS));
   12473             : }
   12474             : 
   12475             : /************************************************************************/
   12476             : /*                      GDALMDArrayGetSpatialRef()                      */
   12477             : /************************************************************************/
   12478             : 
   12479             : /** Return the spatial reference system object associated with the array.
   12480             :  *
   12481             :  * This is the same as the C++ method GDALMDArray::GetSpatialRef().
   12482             :  *
   12483             :  * The returned object must be freed with OSRDestroySpatialReference().
   12484             :  */
   12485          77 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
   12486             : {
   12487          77 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12488          77 :     auto poSRS = hArray->m_poImpl->GetSpatialRef();
   12489          77 :     return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
   12490             : }
   12491             : 
   12492             : /************************************************************************/
   12493             : /*                      GDALMDArrayGetStatistics()                      */
   12494             : /************************************************************************/
   12495             : 
   12496             : /**
   12497             :  * \brief Fetch statistics.
   12498             :  *
   12499             :  * This is the same as the C++ method GDALMDArray::GetStatistics().
   12500             :  *
   12501             :  * @since GDAL 3.2
   12502             :  */
   12503             : 
   12504          15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
   12505             :                                 int bApproxOK, int bForce, double *pdfMin,
   12506             :                                 double *pdfMax, double *pdfMean,
   12507             :                                 double *pdfStdDev, GUInt64 *pnValidCount,
   12508             :                                 GDALProgressFunc pfnProgress,
   12509             :                                 void *pProgressData)
   12510             : {
   12511          15 :     VALIDATE_POINTER1(hArray, __func__, CE_Failure);
   12512          30 :     return hArray->m_poImpl->GetStatistics(
   12513          15 :         CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
   12514          15 :         pdfStdDev, pnValidCount, pfnProgress, pProgressData);
   12515             : }
   12516             : 
   12517             : /************************************************************************/
   12518             : /*                      GDALMDArrayComputeStatistics()                  */
   12519             : /************************************************************************/
   12520             : 
   12521             : /**
   12522             :  * \brief Compute statistics.
   12523             :  *
   12524             :  * This is the same as the C++ method GDALMDArray::ComputeStatistics().
   12525             :  *
   12526             :  * @since GDAL 3.2
   12527             :  * @see GDALMDArrayComputeStatisticsEx()
   12528             :  */
   12529             : 
   12530           0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
   12531             :                                  int bApproxOK, double *pdfMin, double *pdfMax,
   12532             :                                  double *pdfMean, double *pdfStdDev,
   12533             :                                  GUInt64 *pnValidCount,
   12534             :                                  GDALProgressFunc pfnProgress,
   12535             :                                  void *pProgressData)
   12536             : {
   12537           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12538           0 :     return hArray->m_poImpl->ComputeStatistics(
   12539           0 :         CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
   12540           0 :         pnValidCount, pfnProgress, pProgressData, nullptr);
   12541             : }
   12542             : 
   12543             : /************************************************************************/
   12544             : /*                     GDALMDArrayComputeStatisticsEx()                 */
   12545             : /************************************************************************/
   12546             : 
   12547             : /**
   12548             :  * \brief Compute statistics.
   12549             :  *
   12550             :  * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
   12551             :  *
   12552             :  * This is the same as the C++ method GDALMDArray::ComputeStatistics().
   12553             :  *
   12554             :  * @since GDAL 3.8
   12555             :  */
   12556             : 
   12557           4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
   12558             :                                    int bApproxOK, double *pdfMin,
   12559             :                                    double *pdfMax, double *pdfMean,
   12560             :                                    double *pdfStdDev, GUInt64 *pnValidCount,
   12561             :                                    GDALProgressFunc pfnProgress,
   12562             :                                    void *pProgressData,
   12563             :                                    CSLConstList papszOptions)
   12564             : {
   12565           4 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12566           8 :     return hArray->m_poImpl->ComputeStatistics(
   12567           4 :         CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
   12568           8 :         pnValidCount, pfnProgress, pProgressData, papszOptions);
   12569             : }
   12570             : 
   12571             : /************************************************************************/
   12572             : /*                 GDALMDArrayGetCoordinateVariables()                  */
   12573             : /************************************************************************/
   12574             : 
   12575             : /** Return coordinate variables.
   12576             :  *
   12577             :  * The returned array must be freed with GDALReleaseArrays(). If only the array
   12578             :  * itself needs to be freed, CPLFree() should be called (and
   12579             :  * GDALMDArrayRelease() on individual array members).
   12580             :  *
   12581             :  * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
   12582             :  *
   12583             :  * @param hArray Array.
   12584             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12585             :  *
   12586             :  * @return an array of *pnCount arrays.
   12587             :  * @since 3.4
   12588             :  */
   12589          13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
   12590             :                                                 size_t *pnCount)
   12591             : {
   12592          13 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12593          13 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12594          13 :     const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
   12595             :     auto ret = static_cast<GDALMDArrayH *>(
   12596          13 :         CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
   12597          29 :     for (size_t i = 0; i < coordinates.size(); i++)
   12598             :     {
   12599          16 :         ret[i] = new GDALMDArrayHS(coordinates[i]);
   12600             :     }
   12601          13 :     *pnCount = coordinates.size();
   12602          13 :     return ret;
   12603             : }
   12604             : 
   12605             : /************************************************************************/
   12606             : /*                     GDALMDArrayGetGridded()                          */
   12607             : /************************************************************************/
   12608             : 
   12609             : /** Return a gridded array from scattered point data, that is from an array
   12610             :  * whose last dimension is the indexing variable of X and Y arrays.
   12611             :  *
   12612             :  * The returned object should be released with GDALMDArrayRelease().
   12613             :  *
   12614             :  * This is the same as the C++ method GDALMDArray::GetGridded().
   12615             :  *
   12616             :  * @since GDAL 3.7
   12617             :  */
   12618          22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
   12619             :                                    const char *pszGridOptions,
   12620             :                                    GDALMDArrayH hXArray, GDALMDArrayH hYArray,
   12621             :                                    CSLConstList papszOptions)
   12622             : {
   12623          22 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12624          22 :     VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
   12625          22 :     auto gridded = hArray->m_poImpl->GetGridded(
   12626          44 :         pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
   12627          88 :         hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
   12628          22 :     if (!gridded)
   12629          19 :         return nullptr;
   12630           3 :     return new GDALMDArrayHS(gridded);
   12631             : }
   12632             : 
   12633             : /************************************************************************/
   12634             : /*                      GDALMDArrayGetMeshGrid()                        */
   12635             : /************************************************************************/
   12636             : 
   12637             : /** Return a list of multidimensional arrays from a list of one-dimensional
   12638             :  * arrays.
   12639             :  *
   12640             :  * This is typically used to transform one-dimensional longitude, latitude
   12641             :  * arrays into 2D ones.
   12642             :  *
   12643             :  * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
   12644             :  * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
   12645             :  * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
   12646             :  * repeated to fill the matrix along the first dimension for x1, the second
   12647             :  * for x2 and so on.
   12648             :  *
   12649             :  * For example, if x = [1, 2], and y = [3, 4, 5],
   12650             :  * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
   12651             :  * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
   12652             :  * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
   12653             :  *
   12654             :  * and
   12655             :  * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
   12656             :  * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
   12657             :  * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
   12658             :  *
   12659             :  * The currently supported options are:
   12660             :  * <ul>
   12661             :  * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
   12662             :  * output.
   12663             :  * </li>
   12664             :  * </ul>
   12665             :  *
   12666             :  * This is the same as
   12667             :  * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
   12668             :  * function.
   12669             :  *
   12670             :  * The returned array (of arrays) must be freed with GDALReleaseArrays().
   12671             :  * If only the array itself needs to be freed, CPLFree() should be called
   12672             :  * (and GDALMDArrayRelease() on individual array members).
   12673             :  *
   12674             :  * This is the same as the C++ method GDALMDArray::GetMeshGrid()
   12675             :  *
   12676             :  * @param pahInputArrays Input arrays
   12677             :  * @param nCountInputArrays Number of input arrays
   12678             :  * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
   12679             :  * @param papszOptions NULL, or NULL terminated list of options.
   12680             :  *
   12681             :  * @return an array of *pnCountOutputArrays arrays.
   12682             :  * @since 3.10
   12683             :  */
   12684           7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
   12685             :                                      size_t nCountInputArrays,
   12686             :                                      size_t *pnCountOutputArrays,
   12687             :                                      CSLConstList papszOptions)
   12688             : {
   12689           7 :     VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
   12690           7 :     VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
   12691             : 
   12692          14 :     std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
   12693          20 :     for (size_t i = 0; i < nCountInputArrays; ++i)
   12694          13 :         apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
   12695             : 
   12696             :     const auto apoOutputArrays =
   12697           7 :         GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
   12698             :     auto ret = static_cast<GDALMDArrayH *>(
   12699           7 :         CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
   12700          17 :     for (size_t i = 0; i < apoOutputArrays.size(); i++)
   12701             :     {
   12702          10 :         ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
   12703             :     }
   12704           7 :     *pnCountOutputArrays = apoOutputArrays.size();
   12705           7 :     return ret;
   12706             : }
   12707             : 
   12708             : /************************************************************************/
   12709             : /*                        GDALReleaseArrays()                           */
   12710             : /************************************************************************/
   12711             : 
   12712             : /** Free the return of GDALMDArrayGetCoordinateVariables()
   12713             :  *
   12714             :  * @param arrays return pointer of above methods
   12715             :  * @param nCount *pnCount value returned by above methods
   12716             :  */
   12717          20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
   12718             : {
   12719          46 :     for (size_t i = 0; i < nCount; i++)
   12720             :     {
   12721          26 :         delete arrays[i];
   12722             :     }
   12723          20 :     CPLFree(arrays);
   12724          20 : }
   12725             : 
   12726             : /************************************************************************/
   12727             : /*                           GDALMDArrayCache()                         */
   12728             : /************************************************************************/
   12729             : 
   12730             : /**
   12731             :  * \brief Cache the content of the array into an auxiliary filename.
   12732             :  *
   12733             :  * This is the same as the C++ method GDALMDArray::Cache().
   12734             :  *
   12735             :  * @since GDAL 3.4
   12736             :  */
   12737             : 
   12738           7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
   12739             : {
   12740           7 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12741           7 :     return hArray->m_poImpl->Cache(papszOptions);
   12742             : }
   12743             : 
   12744             : /************************************************************************/
   12745             : /*                       GDALMDArrayRename()                           */
   12746             : /************************************************************************/
   12747             : 
   12748             : /** Rename the array.
   12749             :  *
   12750             :  * This is not implemented by all drivers.
   12751             :  *
   12752             :  * Drivers known to implement it: MEM, netCDF, Zarr.
   12753             :  *
   12754             :  * This is the same as the C++ method GDALAbstractMDArray::Rename()
   12755             :  *
   12756             :  * @return true in case of success
   12757             :  * @since GDAL 3.8
   12758             :  */
   12759          28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
   12760             : {
   12761          28 :     VALIDATE_POINTER1(hArray, __func__, false);
   12762          28 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   12763          28 :     return hArray->m_poImpl->Rename(pszNewName);
   12764             : }
   12765             : 
   12766             : /************************************************************************/
   12767             : /*                        GDALAttributeRelease()                        */
   12768             : /************************************************************************/
   12769             : 
   12770             : /** Release the GDAL in-memory object associated with a GDALAttribute.
   12771             :  *
   12772             :  * Note: when applied on a object coming from a driver, this does not
   12773             :  * destroy the object in the file, database, etc...
   12774             :  */
   12775         713 : void GDALAttributeRelease(GDALAttributeH hAttr)
   12776             : {
   12777         713 :     delete hAttr;
   12778         713 : }
   12779             : 
   12780             : /************************************************************************/
   12781             : /*                        GDALAttributeGetName()                        */
   12782             : /************************************************************************/
   12783             : 
   12784             : /** Return the name of the attribute.
   12785             :  *
   12786             :  * The returned pointer is valid until hAttr is released.
   12787             :  *
   12788             :  * This is the same as the C++ method GDALAttribute::GetName().
   12789             :  */
   12790         361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
   12791             : {
   12792         361 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   12793         361 :     return hAttr->m_poImpl->GetName().c_str();
   12794             : }
   12795             : 
   12796             : /************************************************************************/
   12797             : /*                      GDALAttributeGetFullName()                      */
   12798             : /************************************************************************/
   12799             : 
   12800             : /** Return the full name of the attribute.
   12801             :  *
   12802             :  * The returned pointer is valid until hAttr is released.
   12803             :  *
   12804             :  * This is the same as the C++ method GDALAttribute::GetFullName().
   12805             :  */
   12806          49 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
   12807             : {
   12808          49 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   12809          49 :     return hAttr->m_poImpl->GetFullName().c_str();
   12810             : }
   12811             : 
   12812             : /************************************************************************/
   12813             : /*                   GDALAttributeGetTotalElementsCount()               */
   12814             : /************************************************************************/
   12815             : 
   12816             : /** Return the total number of values in the attribute.
   12817             :  *
   12818             :  * This is the same as the C++ method
   12819             :  * GDALAbstractMDArray::GetTotalElementsCount()
   12820             :  */
   12821         176 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
   12822             : {
   12823         176 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   12824         176 :     return hAttr->m_poImpl->GetTotalElementsCount();
   12825             : }
   12826             : 
   12827             : /************************************************************************/
   12828             : /*                    GDALAttributeGetDimensionCount()                */
   12829             : /************************************************************************/
   12830             : 
   12831             : /** Return the number of dimensions.
   12832             :  *
   12833             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
   12834             :  */
   12835          12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
   12836             : {
   12837          12 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   12838          12 :     return hAttr->m_poImpl->GetDimensionCount();
   12839             : }
   12840             : 
   12841             : /************************************************************************/
   12842             : /*                       GDALAttributeGetDimensionsSize()                */
   12843             : /************************************************************************/
   12844             : 
   12845             : /** Return the dimension sizes of the attribute.
   12846             :  *
   12847             :  * The returned array must be freed with CPLFree()
   12848             :  *
   12849             :  * @param hAttr Attribute.
   12850             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12851             :  *
   12852             :  * @return an array of *pnCount values.
   12853             :  */
   12854          11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
   12855             : {
   12856          11 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   12857          11 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12858          11 :     const auto &dims = hAttr->m_poImpl->GetDimensions();
   12859          11 :     auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
   12860          22 :     for (size_t i = 0; i < dims.size(); i++)
   12861             :     {
   12862          11 :         ret[i] = dims[i]->GetSize();
   12863             :     }
   12864          11 :     *pnCount = dims.size();
   12865          11 :     return ret;
   12866             : }
   12867             : 
   12868             : /************************************************************************/
   12869             : /*                       GDALAttributeGetDataType()                     */
   12870             : /************************************************************************/
   12871             : 
   12872             : /** Return the data type
   12873             :  *
   12874             :  * The return must be freed with GDALExtendedDataTypeRelease().
   12875             :  */
   12876         426 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
   12877             : {
   12878         426 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   12879             :     return new GDALExtendedDataTypeHS(
   12880         426 :         new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
   12881             : }
   12882             : 
   12883             : /************************************************************************/
   12884             : /*                       GDALAttributeReadAsRaw()                       */
   12885             : /************************************************************************/
   12886             : 
   12887             : /** Return the raw value of an attribute.
   12888             :  *
   12889             :  * This is the same as the C++ method GDALAttribute::ReadAsRaw().
   12890             :  *
   12891             :  * The returned buffer must be freed with GDALAttributeFreeRawResult()
   12892             :  *
   12893             :  * @param hAttr Attribute.
   12894             :  * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
   12895             :  *
   12896             :  * @return a buffer of *pnSize bytes.
   12897             :  */
   12898           6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
   12899             : {
   12900           6 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   12901           6 :     VALIDATE_POINTER1(pnSize, __func__, nullptr);
   12902          12 :     auto res(hAttr->m_poImpl->ReadAsRaw());
   12903           6 :     *pnSize = res.size();
   12904           6 :     auto ret = res.StealData();
   12905           6 :     if (!ret)
   12906             :     {
   12907           0 :         *pnSize = 0;
   12908           0 :         return nullptr;
   12909             :     }
   12910           6 :     return ret;
   12911             : }
   12912             : 
   12913             : /************************************************************************/
   12914             : /*                       GDALAttributeFreeRawResult()                   */
   12915             : /************************************************************************/
   12916             : 
   12917             : /** Free the return of GDALAttributeAsRaw()
   12918             :  */
   12919           6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
   12920             :                                 CPL_UNUSED size_t nSize)
   12921             : {
   12922           6 :     VALIDATE_POINTER0(hAttr, __func__);
   12923           6 :     if (raw)
   12924             :     {
   12925           6 :         const auto &dt(hAttr->m_poImpl->GetDataType());
   12926           6 :         const auto nDTSize(dt.GetSize());
   12927           6 :         GByte *pabyPtr = raw;
   12928           6 :         const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
   12929           6 :         CPLAssert(nSize == nDTSize * nEltCount);
   12930          12 :         for (size_t i = 0; i < nEltCount; ++i)
   12931             :         {
   12932           6 :             dt.FreeDynamicMemory(pabyPtr);
   12933           6 :             pabyPtr += nDTSize;
   12934             :         }
   12935           6 :         CPLFree(raw);
   12936             :     }
   12937             : }
   12938             : 
   12939             : /************************************************************************/
   12940             : /*                       GDALAttributeReadAsString()                    */
   12941             : /************************************************************************/
   12942             : 
   12943             : /** Return the value of an attribute as a string.
   12944             :  *
   12945             :  * The returned string should not be freed, and its lifetime does not
   12946             :  * excess a next call to ReadAsString() on the same object, or the deletion
   12947             :  * of the object itself.
   12948             :  *
   12949             :  * This function will only return the first element if there are several.
   12950             :  *
   12951             :  * This is the same as the C++ method GDALAttribute::ReadAsString()
   12952             :  *
   12953             :  * @return a string, or nullptr.
   12954             :  */
   12955         107 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
   12956             : {
   12957         107 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   12958         107 :     return hAttr->m_poImpl->ReadAsString();
   12959             : }
   12960             : 
   12961             : /************************************************************************/
   12962             : /*                      GDALAttributeReadAsInt()                        */
   12963             : /************************************************************************/
   12964             : 
   12965             : /** Return the value of an attribute as a integer.
   12966             :  *
   12967             :  * This function will only return the first element if there are several.
   12968             :  *
   12969             :  * It can fail if its value can not be converted to integer.
   12970             :  *
   12971             :  * This is the same as the C++ method GDALAttribute::ReadAsInt()
   12972             :  *
   12973             :  * @return a integer, or INT_MIN in case of error.
   12974             :  */
   12975          22 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
   12976             : {
   12977          22 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   12978          22 :     return hAttr->m_poImpl->ReadAsInt();
   12979             : }
   12980             : 
   12981             : /************************************************************************/
   12982             : /*                      GDALAttributeReadAsInt64()                      */
   12983             : /************************************************************************/
   12984             : 
   12985             : /** Return the value of an attribute as a int64_t.
   12986             :  *
   12987             :  * This function will only return the first element if there are several.
   12988             :  *
   12989             :  * It can fail if its value can not be converted to integer.
   12990             :  *
   12991             :  * This is the same as the C++ method GDALAttribute::ReadAsInt64()
   12992             :  *
   12993             :  * @return an int64_t, or INT64_MIN in case of error.
   12994             :  */
   12995          15 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
   12996             : {
   12997          15 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   12998          15 :     return hAttr->m_poImpl->ReadAsInt64();
   12999             : }
   13000             : 
   13001             : /************************************************************************/
   13002             : /*                       GDALAttributeReadAsDouble()                    */
   13003             : /************************************************************************/
   13004             : 
   13005             : /** Return the value of an attribute as a double.
   13006             :  *
   13007             :  * This function will only return the first element if there are several.
   13008             :  *
   13009             :  * It can fail if its value can not be converted to double.
   13010             :  *
   13011             :  * This is the same as the C++ method GDALAttribute::ReadAsDouble()
   13012             :  *
   13013             :  * @return a double value.
   13014             :  */
   13015          40 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
   13016             : {
   13017          40 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13018          40 :     return hAttr->m_poImpl->ReadAsDouble();
   13019             : }
   13020             : 
   13021             : /************************************************************************/
   13022             : /*                     GDALAttributeReadAsStringArray()                 */
   13023             : /************************************************************************/
   13024             : 
   13025             : /** Return the value of an attribute as an array of strings.
   13026             :  *
   13027             :  * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
   13028             :  *
   13029             :  * The return value must be freed with CSLDestroy().
   13030             :  */
   13031          19 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
   13032             : {
   13033          19 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13034          19 :     return hAttr->m_poImpl->ReadAsStringArray().StealList();
   13035             : }
   13036             : 
   13037             : /************************************************************************/
   13038             : /*                     GDALAttributeReadAsIntArray()                    */
   13039             : /************************************************************************/
   13040             : 
   13041             : /** Return the value of an attribute as an array of integers.
   13042             :  *
   13043             :  * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
   13044             :  *
   13045             :  * @param hAttr Attribute
   13046             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13047             :  * @return array to be freed with CPLFree(), or nullptr.
   13048             :  */
   13049          15 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
   13050             : {
   13051          15 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13052          15 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13053          15 :     *pnCount = 0;
   13054          30 :     auto tmp(hAttr->m_poImpl->ReadAsIntArray());
   13055          15 :     if (tmp.empty())
   13056           0 :         return nullptr;
   13057          15 :     auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
   13058          15 :     if (!ret)
   13059           0 :         return nullptr;
   13060          15 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
   13061          15 :     *pnCount = tmp.size();
   13062          15 :     return ret;
   13063             : }
   13064             : 
   13065             : /************************************************************************/
   13066             : /*                     GDALAttributeReadAsInt64Array()                  */
   13067             : /************************************************************************/
   13068             : 
   13069             : /** Return the value of an attribute as an array of int64_t.
   13070             :  *
   13071             :  * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
   13072             :  *
   13073             :  * @param hAttr Attribute
   13074             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13075             :  * @return array to be freed with CPLFree(), or nullptr.
   13076             :  */
   13077          14 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
   13078             : {
   13079          14 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13080          14 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13081          14 :     *pnCount = 0;
   13082          28 :     auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
   13083          14 :     if (tmp.empty())
   13084           0 :         return nullptr;
   13085             :     auto ret = static_cast<int64_t *>(
   13086          14 :         VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
   13087          14 :     if (!ret)
   13088           0 :         return nullptr;
   13089          14 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
   13090          14 :     *pnCount = tmp.size();
   13091          14 :     return ret;
   13092             : }
   13093             : 
   13094             : /************************************************************************/
   13095             : /*                     GDALAttributeReadAsDoubleArray()                 */
   13096             : /************************************************************************/
   13097             : 
   13098             : /** Return the value of an attribute as an array of doubles.
   13099             :  *
   13100             :  * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
   13101             :  *
   13102             :  * @param hAttr Attribute
   13103             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13104             :  * @return array to be freed with CPLFree(), or nullptr.
   13105             :  */
   13106          29 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
   13107             : {
   13108          29 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13109          29 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13110          29 :     *pnCount = 0;
   13111          58 :     auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
   13112          29 :     if (tmp.empty())
   13113           0 :         return nullptr;
   13114             :     auto ret =
   13115          29 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
   13116          29 :     if (!ret)
   13117           0 :         return nullptr;
   13118          29 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
   13119          29 :     *pnCount = tmp.size();
   13120          29 :     return ret;
   13121             : }
   13122             : 
   13123             : /************************************************************************/
   13124             : /*                     GDALAttributeWriteRaw()                          */
   13125             : /************************************************************************/
   13126             : 
   13127             : /** Write an attribute from raw values expressed in GetDataType()
   13128             :  *
   13129             :  * The values should be provided in the type of GetDataType() and there should
   13130             :  * be exactly GetTotalElementsCount() of them.
   13131             :  * If GetDataType() is a string, each value should be a char* pointer.
   13132             :  *
   13133             :  * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
   13134             :  *
   13135             :  * @param hAttr Attribute
   13136             :  * @param pabyValue Buffer of nLen bytes.
   13137             :  * @param nLength Size of pabyValue in bytes. Should be equal to
   13138             :  *             GetTotalElementsCount() * GetDataType().GetSize()
   13139             :  * @return TRUE in case of success.
   13140             :  */
   13141           5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
   13142             :                           size_t nLength)
   13143             : {
   13144           5 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13145           5 :     return hAttr->m_poImpl->Write(pabyValue, nLength);
   13146             : }
   13147             : 
   13148             : /************************************************************************/
   13149             : /*                     GDALAttributeWriteString()                       */
   13150             : /************************************************************************/
   13151             : 
   13152             : /** Write an attribute from a string value.
   13153             :  *
   13154             :  * Type conversion will be performed if needed. If the attribute contains
   13155             :  * multiple values, only the first one will be updated.
   13156             :  *
   13157             :  * This is the same as the C++ method GDALAttribute::Write(const char*)
   13158             :  *
   13159             :  * @param hAttr Attribute
   13160             :  * @param pszVal Pointer to a string.
   13161             :  * @return TRUE in case of success.
   13162             :  */
   13163         174 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
   13164             : {
   13165         174 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13166         174 :     return hAttr->m_poImpl->Write(pszVal);
   13167             : }
   13168             : 
   13169             : /************************************************************************/
   13170             : /*                        GDALAttributeWriteInt()                       */
   13171             : /************************************************************************/
   13172             : 
   13173             : /** Write an attribute from a integer value.
   13174             :  *
   13175             :  * Type conversion will be performed if needed. If the attribute contains
   13176             :  * multiple values, only the first one will be updated.
   13177             :  *
   13178             :  * This is the same as the C++ method GDALAttribute::WriteInt()
   13179             :  *
   13180             :  * @param hAttr Attribute
   13181             :  * @param nVal Value.
   13182             :  * @return TRUE in case of success.
   13183             :  */
   13184          22 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
   13185             : {
   13186          22 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13187          22 :     return hAttr->m_poImpl->WriteInt(nVal);
   13188             : }
   13189             : 
   13190             : /************************************************************************/
   13191             : /*                        GDALAttributeWriteInt64()                     */
   13192             : /************************************************************************/
   13193             : 
   13194             : /** Write an attribute from an int64_t value.
   13195             :  *
   13196             :  * Type conversion will be performed if needed. If the attribute contains
   13197             :  * multiple values, only the first one will be updated.
   13198             :  *
   13199             :  * This is the same as the C++ method GDALAttribute::WriteLong()
   13200             :  *
   13201             :  * @param hAttr Attribute
   13202             :  * @param nVal Value.
   13203             :  * @return TRUE in case of success.
   13204             :  */
   13205          11 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
   13206             : {
   13207          11 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13208          11 :     return hAttr->m_poImpl->WriteInt64(nVal);
   13209             : }
   13210             : 
   13211             : /************************************************************************/
   13212             : /*                        GDALAttributeWriteDouble()                    */
   13213             : /************************************************************************/
   13214             : 
   13215             : /** Write an attribute from a double value.
   13216             :  *
   13217             :  * Type conversion will be performed if needed. If the attribute contains
   13218             :  * multiple values, only the first one will be updated.
   13219             :  *
   13220             :  * This is the same as the C++ method GDALAttribute::Write(double);
   13221             :  *
   13222             :  * @param hAttr Attribute
   13223             :  * @param dfVal Value.
   13224             :  *
   13225             :  * @return TRUE in case of success.
   13226             :  */
   13227          11 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
   13228             : {
   13229          11 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13230          11 :     return hAttr->m_poImpl->Write(dfVal);
   13231             : }
   13232             : 
   13233             : /************************************************************************/
   13234             : /*                       GDALAttributeWriteStringArray()                */
   13235             : /************************************************************************/
   13236             : 
   13237             : /** Write an attribute from an array of strings.
   13238             :  *
   13239             :  * Type conversion will be performed if needed.
   13240             :  *
   13241             :  * Exactly GetTotalElementsCount() strings must be provided
   13242             :  *
   13243             :  * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
   13244             :  *
   13245             :  * @param hAttr Attribute
   13246             :  * @param papszValues Array of strings.
   13247             :  * @return TRUE in case of success.
   13248             :  */
   13249           8 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
   13250             :                                   CSLConstList papszValues)
   13251             : {
   13252           8 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13253           8 :     return hAttr->m_poImpl->Write(papszValues);
   13254             : }
   13255             : 
   13256             : /************************************************************************/
   13257             : /*                       GDALAttributeWriteIntArray()                */
   13258             : /************************************************************************/
   13259             : 
   13260             : /** Write an attribute from an array of int.
   13261             :  *
   13262             :  * Type conversion will be performed if needed.
   13263             :  *
   13264             :  * Exactly GetTotalElementsCount() strings must be provided
   13265             :  *
   13266             :  * This is the same as the C++ method GDALAttribute::Write(const int *,
   13267             :  * size_t)
   13268             :  *
   13269             :  * @param hAttr Attribute
   13270             :  * @param panValues Array of int.
   13271             :  * @param nCount Should be equal to GetTotalElementsCount().
   13272             :  * @return TRUE in case of success.
   13273             :  */
   13274           9 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
   13275             :                                size_t nCount)
   13276             : {
   13277           9 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13278           9 :     return hAttr->m_poImpl->Write(panValues, nCount);
   13279             : }
   13280             : 
   13281             : /************************************************************************/
   13282             : /*                       GDALAttributeWriteInt64Array()                 */
   13283             : /************************************************************************/
   13284             : 
   13285             : /** Write an attribute from an array of int64_t.
   13286             :  *
   13287             :  * Type conversion will be performed if needed.
   13288             :  *
   13289             :  * Exactly GetTotalElementsCount() strings must be provided
   13290             :  *
   13291             :  * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
   13292             :  * size_t)
   13293             :  *
   13294             :  * @param hAttr Attribute
   13295             :  * @param panValues Array of int64_t.
   13296             :  * @param nCount Should be equal to GetTotalElementsCount().
   13297             :  * @return TRUE in case of success.
   13298             :  */
   13299          10 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
   13300             :                                  size_t nCount)
   13301             : {
   13302          10 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13303          10 :     return hAttr->m_poImpl->Write(panValues, nCount);
   13304             : }
   13305             : 
   13306             : /************************************************************************/
   13307             : /*                       GDALAttributeWriteDoubleArray()                */
   13308             : /************************************************************************/
   13309             : 
   13310             : /** Write an attribute from an array of double.
   13311             :  *
   13312             :  * Type conversion will be performed if needed.
   13313             :  *
   13314             :  * Exactly GetTotalElementsCount() strings must be provided
   13315             :  *
   13316             :  * This is the same as the C++ method GDALAttribute::Write(const double *,
   13317             :  * size_t)
   13318             :  *
   13319             :  * @param hAttr Attribute
   13320             :  * @param padfValues Array of double.
   13321             :  * @param nCount Should be equal to GetTotalElementsCount().
   13322             :  * @return TRUE in case of success.
   13323             :  */
   13324           7 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
   13325             :                                   const double *padfValues, size_t nCount)
   13326             : {
   13327           7 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13328           7 :     return hAttr->m_poImpl->Write(padfValues, nCount);
   13329             : }
   13330             : 
   13331             : /************************************************************************/
   13332             : /*                      GDALAttributeRename()                           */
   13333             : /************************************************************************/
   13334             : 
   13335             : /** Rename the attribute.
   13336             :  *
   13337             :  * This is not implemented by all drivers.
   13338             :  *
   13339             :  * Drivers known to implement it: MEM, netCDF.
   13340             :  *
   13341             :  * This is the same as the C++ method GDALAbstractMDArray::Rename()
   13342             :  *
   13343             :  * @return true in case of success
   13344             :  * @since GDAL 3.8
   13345             :  */
   13346          27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
   13347             : {
   13348          27 :     VALIDATE_POINTER1(hAttr, __func__, false);
   13349          27 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   13350          27 :     return hAttr->m_poImpl->Rename(pszNewName);
   13351             : }
   13352             : 
   13353             : /************************************************************************/
   13354             : /*                        GDALDimensionRelease()                        */
   13355             : /************************************************************************/
   13356             : 
   13357             : /** Release the GDAL in-memory object associated with a GDALDimension.
   13358             :  *
   13359             :  * Note: when applied on a object coming from a driver, this does not
   13360             :  * destroy the object in the file, database, etc...
   13361             :  */
   13362        4836 : void GDALDimensionRelease(GDALDimensionH hDim)
   13363             : {
   13364        4836 :     delete hDim;
   13365        4836 : }
   13366             : 
   13367             : /************************************************************************/
   13368             : /*                        GDALDimensionGetName()                        */
   13369             : /************************************************************************/
   13370             : 
   13371             : /** Return dimension name.
   13372             :  *
   13373             :  * This is the same as the C++ method GDALDimension::GetName()
   13374             :  */
   13375         274 : const char *GDALDimensionGetName(GDALDimensionH hDim)
   13376             : {
   13377         274 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13378         274 :     return hDim->m_poImpl->GetName().c_str();
   13379             : }
   13380             : 
   13381             : /************************************************************************/
   13382             : /*                      GDALDimensionGetFullName()                      */
   13383             : /************************************************************************/
   13384             : 
   13385             : /** Return dimension full name.
   13386             :  *
   13387             :  * This is the same as the C++ method GDALDimension::GetFullName()
   13388             :  */
   13389          80 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
   13390             : {
   13391          80 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13392          80 :     return hDim->m_poImpl->GetFullName().c_str();
   13393             : }
   13394             : 
   13395             : /************************************************************************/
   13396             : /*                        GDALDimensionGetType()                        */
   13397             : /************************************************************************/
   13398             : 
   13399             : /** Return dimension type.
   13400             :  *
   13401             :  * This is the same as the C++ method GDALDimension::GetType()
   13402             :  */
   13403          52 : const char *GDALDimensionGetType(GDALDimensionH hDim)
   13404             : {
   13405          52 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13406          52 :     return hDim->m_poImpl->GetType().c_str();
   13407             : }
   13408             : 
   13409             : /************************************************************************/
   13410             : /*                     GDALDimensionGetDirection()                      */
   13411             : /************************************************************************/
   13412             : 
   13413             : /** Return dimension direction.
   13414             :  *
   13415             :  * This is the same as the C++ method GDALDimension::GetDirection()
   13416             :  */
   13417          22 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
   13418             : {
   13419          22 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13420          22 :     return hDim->m_poImpl->GetDirection().c_str();
   13421             : }
   13422             : 
   13423             : /************************************************************************/
   13424             : /*                        GDALDimensionGetSize()                        */
   13425             : /************************************************************************/
   13426             : 
   13427             : /** Return the size, that is the number of values along the dimension.
   13428             :  *
   13429             :  * This is the same as the C++ method GDALDimension::GetSize()
   13430             :  */
   13431        3616 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
   13432             : {
   13433        3616 :     VALIDATE_POINTER1(hDim, __func__, 0);
   13434        3616 :     return hDim->m_poImpl->GetSize();
   13435             : }
   13436             : 
   13437             : /************************************************************************/
   13438             : /*                     GDALDimensionGetIndexingVariable()               */
   13439             : /************************************************************************/
   13440             : 
   13441             : /** Return the variable that is used to index the dimension (if there is one).
   13442             :  *
   13443             :  * This is the array, typically one-dimensional, describing the values taken
   13444             :  * by the dimension.
   13445             :  *
   13446             :  * The returned value should be freed with GDALMDArrayRelease().
   13447             :  *
   13448             :  * This is the same as the C++ method GDALDimension::GetIndexingVariable()
   13449             :  */
   13450         118 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
   13451             : {
   13452         118 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13453         236 :     auto var(hDim->m_poImpl->GetIndexingVariable());
   13454         118 :     if (!var)
   13455          10 :         return nullptr;
   13456         108 :     return new GDALMDArrayHS(var);
   13457             : }
   13458             : 
   13459             : /************************************************************************/
   13460             : /*                      GDALDimensionSetIndexingVariable()              */
   13461             : /************************************************************************/
   13462             : 
   13463             : /** Set the variable that is used to index the dimension.
   13464             :  *
   13465             :  * This is the array, typically one-dimensional, describing the values taken
   13466             :  * by the dimension.
   13467             :  *
   13468             :  * This is the same as the C++ method GDALDimension::SetIndexingVariable()
   13469             :  *
   13470             :  * @return TRUE in case of success.
   13471             :  */
   13472          22 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
   13473             : {
   13474          22 :     VALIDATE_POINTER1(hDim, __func__, FALSE);
   13475          66 :     return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
   13476          44 :                                                       : nullptr);
   13477             : }
   13478             : 
   13479             : /************************************************************************/
   13480             : /*                      GDALDimensionRename()                           */
   13481             : /************************************************************************/
   13482             : 
   13483             : /** Rename the dimension.
   13484             :  *
   13485             :  * This is not implemented by all drivers.
   13486             :  *
   13487             :  * Drivers known to implement it: MEM, netCDF.
   13488             :  *
   13489             :  * This is the same as the C++ method GDALDimension::Rename()
   13490             :  *
   13491             :  * @return true in case of success
   13492             :  * @since GDAL 3.8
   13493             :  */
   13494          31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
   13495             : {
   13496          31 :     VALIDATE_POINTER1(hDim, __func__, false);
   13497          31 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   13498          31 :     return hDim->m_poImpl->Rename(pszNewName);
   13499             : }
   13500             : 
   13501             : /************************************************************************/
   13502             : /*                       GDALDatasetGetRootGroup()                      */
   13503             : /************************************************************************/
   13504             : 
   13505             : /** Return the root GDALGroup of this dataset.
   13506             :  *
   13507             :  * Only valid for multidimensional datasets.
   13508             :  *
   13509             :  * The returned value must be freed with GDALGroupRelease().
   13510             :  *
   13511             :  * This is the same as the C++ method GDALDataset::GetRootGroup().
   13512             :  *
   13513             :  * @since GDAL 3.1
   13514             :  */
   13515        1124 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
   13516             : {
   13517        1124 :     VALIDATE_POINTER1(hDS, __func__, nullptr);
   13518        1124 :     auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
   13519        1124 :     return poGroup ? new GDALGroupHS(poGroup) : nullptr;
   13520             : }
   13521             : 
   13522             : /************************************************************************/
   13523             : /*                      GDALRasterBandAsMDArray()                        */
   13524             : /************************************************************************/
   13525             : 
   13526             : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
   13527             :  *
   13528             :  * The band must be linked to a GDALDataset. If this dataset is not already
   13529             :  * marked as shared, it will be, so that the returned array holds a reference
   13530             :  * to it.
   13531             :  *
   13532             :  * If the dataset has a geotransform attached, the X and Y dimensions of the
   13533             :  * returned array will have an associated indexing variable.
   13534             :  *
   13535             :  * The returned pointer must be released with GDALMDArrayRelease().
   13536             :  *
   13537             :  * This is the same as the C++ method GDALRasterBand::AsMDArray().
   13538             :  *
   13539             :  * @return a new array, or NULL.
   13540             :  *
   13541             :  * @since GDAL 3.1
   13542             :  */
   13543          21 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
   13544             : {
   13545          21 :     VALIDATE_POINTER1(hBand, __func__, nullptr);
   13546          42 :     auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
   13547          21 :     if (!poArray)
   13548           0 :         return nullptr;
   13549          21 :     return new GDALMDArrayHS(poArray);
   13550             : }
   13551             : 
   13552             : /************************************************************************/
   13553             : /*                       GDALMDArrayAsClassicDataset()                  */
   13554             : /************************************************************************/
   13555             : 
   13556             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   13557             :  *
   13558             :  * Only 2D or more arrays are supported.
   13559             :  *
   13560             :  * In the case of > 2D arrays, additional dimensions will be represented as
   13561             :  * raster bands.
   13562             :  *
   13563             :  * The "reverse" method is GDALRasterBand::AsMDArray().
   13564             :  *
   13565             :  * This is the same as the C++ method GDALMDArray::AsClassicDataset().
   13566             :  *
   13567             :  * @param hArray Array.
   13568             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   13569             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   13570             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   13571             :  */
   13572           0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
   13573             :                                          size_t iYDim)
   13574             : {
   13575           0 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13576           0 :     return GDALDataset::ToHandle(
   13577           0 :         hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
   13578             : }
   13579             : 
   13580             : /************************************************************************/
   13581             : /*                     GDALMDArrayAsClassicDatasetEx()                  */
   13582             : /************************************************************************/
   13583             : 
   13584             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   13585             :  *
   13586             :  * Only 2D or more arrays are supported.
   13587             :  *
   13588             :  * In the case of > 2D arrays, additional dimensions will be represented as
   13589             :  * raster bands.
   13590             :  *
   13591             :  * The "reverse" method is GDALRasterBand::AsMDArray().
   13592             :  *
   13593             :  * This is the same as the C++ method GDALMDArray::AsClassicDataset().
   13594             :  * @param hArray Array.
   13595             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   13596             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   13597             :  *              Ignored if the dimension count is 1.
   13598             :  * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA option.
   13599             :  * @param papszOptions Cf GDALMDArray::AsClassicDataset()
   13600             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   13601             :  * @since GDAL 3.8
   13602             :  */
   13603          59 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
   13604             :                                            size_t iYDim, GDALGroupH hRootGroup,
   13605             :                                            CSLConstList papszOptions)
   13606             : {
   13607          59 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13608         118 :     return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
   13609         118 :         iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
   13610         118 :         papszOptions));
   13611             : }
   13612             : 
   13613             : //! @cond Doxygen_Suppress
   13614             : 
   13615         180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
   13616             :                                          const std::string &osName,
   13617             :                                          const std::string &osValue,
   13618         180 :                                          GDALExtendedDataTypeSubType eSubType)
   13619             :     : GDALAbstractMDArray(osParentName, osName),
   13620             :       GDALAttribute(osParentName, osName),
   13621         180 :       m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
   13622             : {
   13623         180 : }
   13624             : 
   13625             : const std::vector<std::shared_ptr<GDALDimension>> &
   13626          30 : GDALAttributeString::GetDimensions() const
   13627             : {
   13628          30 :     return m_dims;
   13629             : }
   13630             : 
   13631          21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
   13632             : {
   13633          21 :     return m_dt;
   13634             : }
   13635             : 
   13636          10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
   13637             :                                 const GPtrDiff_t *,
   13638             :                                 const GDALExtendedDataType &bufferDataType,
   13639             :                                 void *pDstBuffer) const
   13640             : {
   13641          10 :     if (bufferDataType.GetClass() != GEDTC_STRING)
   13642           0 :         return false;
   13643          10 :     char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
   13644          10 :     if (!pszStr)
   13645           0 :         return false;
   13646          10 :     memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
   13647          10 :     *static_cast<char **>(pDstBuffer) = pszStr;
   13648          10 :     return true;
   13649             : }
   13650             : 
   13651          66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   13652             :                                            const std::string &osName,
   13653          66 :                                            double dfValue)
   13654             :     : GDALAbstractMDArray(osParentName, osName),
   13655             :       GDALAttribute(osParentName, osName),
   13656          66 :       m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
   13657             : {
   13658          66 : }
   13659             : 
   13660          27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   13661             :                                            const std::string &osName,
   13662          27 :                                            int nValue)
   13663             :     : GDALAbstractMDArray(osParentName, osName),
   13664             :       GDALAttribute(osParentName, osName),
   13665          27 :       m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
   13666             : {
   13667          27 : }
   13668             : 
   13669           7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   13670             :                                            const std::string &osName,
   13671           7 :                                            const std::vector<GUInt32> &anValues)
   13672             :     : GDALAbstractMDArray(osParentName, osName),
   13673             :       GDALAttribute(osParentName, osName),
   13674           7 :       m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
   13675             : {
   13676           7 :     m_dims.push_back(std::make_shared<GDALDimension>(
   13677          14 :         std::string(), "dim0", std::string(), std::string(),
   13678           7 :         m_anValuesUInt32.size()));
   13679           7 : }
   13680             : 
   13681             : const std::vector<std::shared_ptr<GDALDimension>> &
   13682          14 : GDALAttributeNumeric::GetDimensions() const
   13683             : {
   13684          14 :     return m_dims;
   13685             : }
   13686             : 
   13687           8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
   13688             : {
   13689           8 :     return m_dt;
   13690             : }
   13691             : 
   13692           4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
   13693             :                                  const size_t *count, const GInt64 *arrayStep,
   13694             :                                  const GPtrDiff_t *bufferStride,
   13695             :                                  const GDALExtendedDataType &bufferDataType,
   13696             :                                  void *pDstBuffer) const
   13697             : {
   13698           4 :     if (m_dims.empty())
   13699             :     {
   13700           3 :         if (m_dt.GetNumericDataType() == GDT_Float64)
   13701           0 :             GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
   13702             :                                             bufferDataType);
   13703             :         else
   13704             :         {
   13705           3 :             CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
   13706           3 :             GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
   13707             :                                             bufferDataType);
   13708             :         }
   13709             :     }
   13710             :     else
   13711             :     {
   13712           1 :         CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
   13713           1 :         GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
   13714          30 :         for (size_t i = 0; i < count[0]; ++i)
   13715             :         {
   13716          29 :             GDALExtendedDataType::CopyValue(
   13717          29 :                 &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
   13718          29 :                                                       i * arrayStep[0])],
   13719          29 :                 m_dt, pabyDstBuffer, bufferDataType);
   13720          29 :             pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
   13721             :         }
   13722             :     }
   13723           4 :     return true;
   13724             : }
   13725             : 
   13726         192 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
   13727             :     const std::string &osParentName, const std::string &osName,
   13728             :     const std::shared_ptr<GDALDimension> &poDim, double dfStart,
   13729         192 :     double dfIncrement, double dfOffsetInIncrement)
   13730             :     : GDALAbstractMDArray(osParentName, osName),
   13731             :       GDALMDArray(osParentName, osName), m_dfStart(dfStart),
   13732             :       m_dfIncrement(dfIncrement),
   13733         384 :       m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
   13734             : {
   13735         192 : }
   13736             : 
   13737         192 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
   13738             :     const std::string &osParentName, const std::string &osName,
   13739             :     const std::shared_ptr<GDALDimension> &poDim, double dfStart,
   13740             :     double dfIncrement, double dfOffsetInIncrement)
   13741             : {
   13742             :     auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
   13743         192 :         osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
   13744         192 :     poArray->SetSelf(poArray);
   13745         192 :     return poArray;
   13746             : }
   13747             : 
   13748             : const std::vector<std::shared_ptr<GDALDimension>> &
   13749         786 : GDALMDArrayRegularlySpaced::GetDimensions() const
   13750             : {
   13751         786 :     return m_dims;
   13752             : }
   13753             : 
   13754         316 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
   13755             : {
   13756         316 :     return m_dt;
   13757             : }
   13758             : 
   13759             : std::vector<std::shared_ptr<GDALAttribute>>
   13760           4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
   13761             : {
   13762           4 :     return m_attributes;
   13763             : }
   13764             : 
   13765           0 : void GDALMDArrayRegularlySpaced::AddAttribute(
   13766             :     const std::shared_ptr<GDALAttribute> &poAttr)
   13767             : {
   13768           0 :     m_attributes.emplace_back(poAttr);
   13769           0 : }
   13770             : 
   13771         188 : bool GDALMDArrayRegularlySpaced::IRead(
   13772             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
   13773             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
   13774             :     void *pDstBuffer) const
   13775             : {
   13776         188 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
   13777       14719 :     for (size_t i = 0; i < count[0]; i++)
   13778             :     {
   13779       14531 :         const double dfVal = m_dfStart + (arrayStartIdx[0] + i * arrayStep[0] +
   13780       14531 :                                           m_dfOffsetInIncrement) *
   13781       14531 :                                              m_dfIncrement;
   13782       14531 :         GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
   13783             :                                         bufferDataType);
   13784       14531 :         pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
   13785             :     }
   13786         188 :     return true;
   13787             : }
   13788             : 
   13789        2873 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
   13790             :     const std::string &osParentName, const std::string &osName,
   13791        2873 :     const std::string &osType, const std::string &osDirection, GUInt64 nSize)
   13792        2873 :     : GDALDimension(osParentName, osName, osType, osDirection, nSize)
   13793             : {
   13794        2873 : }
   13795             : 
   13796             : std::shared_ptr<GDALMDArray>
   13797         686 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
   13798             : {
   13799         686 :     return m_poIndexingVariable.lock();
   13800             : }
   13801             : 
   13802             : // cppcheck-suppress passedByValue
   13803         483 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
   13804             :     std::shared_ptr<GDALMDArray> poIndexingVariable)
   13805             : {
   13806         483 :     m_poIndexingVariable = poIndexingVariable;
   13807         483 :     return true;
   13808             : }
   13809             : 
   13810          33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
   13811             : {
   13812          33 :     m_nSize = nNewSize;
   13813          33 : }
   13814             : 
   13815             : /************************************************************************/
   13816             : /*                       GDALPamMultiDim::Private                       */
   13817             : /************************************************************************/
   13818             : 
   13819             : struct GDALPamMultiDim::Private
   13820             : {
   13821             :     std::string m_osFilename{};
   13822             :     std::string m_osPamFilename{};
   13823             : 
   13824             :     struct Statistics
   13825             :     {
   13826             :         bool bHasStats = false;
   13827             :         bool bApproxStats = false;
   13828             :         double dfMin = 0;
   13829             :         double dfMax = 0;
   13830             :         double dfMean = 0;
   13831             :         double dfStdDev = 0;
   13832             :         GUInt64 nValidCount = 0;
   13833             :     };
   13834             : 
   13835             :     struct ArrayInfo
   13836             :     {
   13837             :         std::shared_ptr<OGRSpatialReference> poSRS{};
   13838             :         // cppcheck-suppress unusedStructMember
   13839             :         Statistics stats{};
   13840             :     };
   13841             : 
   13842             :     typedef std::pair<std::string, std::string> NameContext;
   13843             :     std::map<NameContext, ArrayInfo> m_oMapArray{};
   13844             :     std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
   13845             :     bool m_bDirty = false;
   13846             :     bool m_bLoaded = false;
   13847             : };
   13848             : 
   13849             : /************************************************************************/
   13850             : /*                          GDALPamMultiDim                             */
   13851             : /************************************************************************/
   13852             : 
   13853        1363 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
   13854        1363 :     : d(new Private())
   13855             : {
   13856        1363 :     d->m_osFilename = osFilename;
   13857        1363 : }
   13858             : 
   13859             : /************************************************************************/
   13860             : /*                   GDALPamMultiDim::~GDALPamMultiDim()                */
   13861             : /************************************************************************/
   13862             : 
   13863        1363 : GDALPamMultiDim::~GDALPamMultiDim()
   13864             : {
   13865        1363 :     if (d->m_bDirty)
   13866          29 :         Save();
   13867        1363 : }
   13868             : 
   13869             : /************************************************************************/
   13870             : /*                          GDALPamMultiDim::Load()                     */
   13871             : /************************************************************************/
   13872             : 
   13873         101 : void GDALPamMultiDim::Load()
   13874             : {
   13875         101 :     if (d->m_bLoaded)
   13876          90 :         return;
   13877          44 :     d->m_bLoaded = true;
   13878             : 
   13879          44 :     const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
   13880          44 :     d->m_osPamFilename =
   13881          88 :         pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
   13882          44 :     CPLXMLTreeCloser oTree(nullptr);
   13883             :     {
   13884          88 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
   13885          44 :         oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
   13886             :     }
   13887          44 :     if (!oTree)
   13888             :     {
   13889          33 :         return;
   13890             :     }
   13891          11 :     const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
   13892          11 :     if (!poPAMMultiDim)
   13893           0 :         return;
   13894          35 :     for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
   13895          24 :          psIter = psIter->psNext)
   13896             :     {
   13897          24 :         if (psIter->eType == CXT_Element &&
   13898          24 :             strcmp(psIter->pszValue, "Array") == 0)
   13899             :         {
   13900          13 :             const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
   13901          13 :             if (!pszName)
   13902           0 :                 continue;
   13903          13 :             const char *pszContext = CPLGetXMLValue(psIter, "context", "");
   13904             :             const auto oKey =
   13905          26 :                 std::pair<std::string, std::string>(pszName, pszContext);
   13906             : 
   13907             :             /* --------------------------------------------------------------------
   13908             :              */
   13909             :             /*      Check for an SRS node. */
   13910             :             /* --------------------------------------------------------------------
   13911             :              */
   13912          13 :             const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
   13913          13 :             if (psSRSNode)
   13914             :             {
   13915             :                 std::shared_ptr<OGRSpatialReference> poSRS =
   13916           6 :                     std::make_shared<OGRSpatialReference>();
   13917           3 :                 poSRS->SetFromUserInput(
   13918             :                     CPLGetXMLValue(psSRSNode, nullptr, ""),
   13919             :                     OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
   13920           3 :                 const char *pszMapping = CPLGetXMLValue(
   13921             :                     psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
   13922           3 :                 if (pszMapping)
   13923             :                 {
   13924             :                     char **papszTokens =
   13925           3 :                         CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
   13926           6 :                     std::vector<int> anMapping;
   13927           9 :                     for (int i = 0; papszTokens && papszTokens[i]; i++)
   13928             :                     {
   13929           6 :                         anMapping.push_back(atoi(papszTokens[i]));
   13930             :                     }
   13931           3 :                     CSLDestroy(papszTokens);
   13932           3 :                     poSRS->SetDataAxisToSRSAxisMapping(anMapping);
   13933             :                 }
   13934             :                 else
   13935             :                 {
   13936           0 :                     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
   13937             :                 }
   13938             : 
   13939             :                 const char *pszCoordinateEpoch =
   13940           3 :                     CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
   13941           3 :                 if (pszCoordinateEpoch)
   13942           3 :                     poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
   13943             : 
   13944           3 :                 d->m_oMapArray[oKey].poSRS = std::move(poSRS);
   13945             :             }
   13946             : 
   13947             :             const CPLXMLNode *psStatistics =
   13948          13 :                 CPLGetXMLNode(psIter, "Statistics");
   13949          13 :             if (psStatistics)
   13950             :             {
   13951           7 :                 Private::Statistics sStats;
   13952           7 :                 sStats.bHasStats = true;
   13953           7 :                 sStats.bApproxStats = CPLTestBool(
   13954             :                     CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
   13955           7 :                 sStats.dfMin =
   13956           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
   13957           7 :                 sStats.dfMax =
   13958           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
   13959           7 :                 sStats.dfMean =
   13960           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
   13961           7 :                 sStats.dfStdDev =
   13962           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
   13963           7 :                 sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
   13964             :                     CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
   13965           7 :                 d->m_oMapArray[oKey].stats = sStats;
   13966          13 :             }
   13967             :         }
   13968             :         else
   13969             :         {
   13970          11 :             CPLXMLNode *psNextBackup = psIter->psNext;
   13971          11 :             psIter->psNext = nullptr;
   13972          11 :             d->m_apoOtherNodes.emplace_back(
   13973          11 :                 CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
   13974          11 :             psIter->psNext = psNextBackup;
   13975             :         }
   13976             :     }
   13977             : }
   13978             : 
   13979             : /************************************************************************/
   13980             : /*                          GDALPamMultiDim::Save()                     */
   13981             : /************************************************************************/
   13982             : 
   13983          29 : void GDALPamMultiDim::Save()
   13984             : {
   13985             :     CPLXMLTreeCloser oTree(
   13986          58 :         CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
   13987          33 :     for (const auto &poOtherNode : d->m_apoOtherNodes)
   13988             :     {
   13989           4 :         CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
   13990             :     }
   13991         108 :     for (const auto &kv : d->m_oMapArray)
   13992             :     {
   13993             :         CPLXMLNode *psArrayNode =
   13994          79 :             CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
   13995          79 :         CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
   13996          79 :         if (!kv.first.second.empty())
   13997             :         {
   13998           1 :             CPLAddXMLAttributeAndValue(psArrayNode, "context",
   13999             :                                        kv.first.second.c_str());
   14000             :         }
   14001          79 :         if (kv.second.poSRS)
   14002             :         {
   14003          71 :             char *pszWKT = nullptr;
   14004             :             {
   14005         142 :                 CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
   14006          71 :                 const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
   14007          71 :                 kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
   14008             :             }
   14009             :             CPLXMLNode *psSRSNode =
   14010          71 :                 CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
   14011          71 :             CPLFree(pszWKT);
   14012             :             const auto &mapping =
   14013          71 :                 kv.second.poSRS->GetDataAxisToSRSAxisMapping();
   14014         142 :             CPLString osMapping;
   14015         213 :             for (size_t i = 0; i < mapping.size(); ++i)
   14016             :             {
   14017         142 :                 if (!osMapping.empty())
   14018          71 :                     osMapping += ",";
   14019         142 :                 osMapping += CPLSPrintf("%d", mapping[i]);
   14020             :             }
   14021          71 :             CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
   14022             :                                        osMapping.c_str());
   14023             : 
   14024             :             const double dfCoordinateEpoch =
   14025          71 :                 kv.second.poSRS->GetCoordinateEpoch();
   14026          71 :             if (dfCoordinateEpoch > 0)
   14027             :             {
   14028             :                 std::string osCoordinateEpoch =
   14029           2 :                     CPLSPrintf("%f", dfCoordinateEpoch);
   14030           1 :                 if (osCoordinateEpoch.find('.') != std::string::npos)
   14031             :                 {
   14032           6 :                     while (osCoordinateEpoch.back() == '0')
   14033           5 :                         osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
   14034             :                 }
   14035           1 :                 CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
   14036             :                                            osCoordinateEpoch.c_str());
   14037             :             }
   14038             :         }
   14039             : 
   14040          79 :         if (kv.second.stats.bHasStats)
   14041             :         {
   14042             :             CPLXMLNode *psMDArray =
   14043           5 :                 CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
   14044           5 :             CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
   14045           5 :                                         kv.second.stats.bApproxStats ? "1"
   14046             :                                                                      : "0");
   14047           5 :             CPLCreateXMLElementAndValue(
   14048             :                 psMDArray, "Minimum",
   14049           5 :                 CPLSPrintf("%.17g", kv.second.stats.dfMin));
   14050           5 :             CPLCreateXMLElementAndValue(
   14051             :                 psMDArray, "Maximum",
   14052           5 :                 CPLSPrintf("%.17g", kv.second.stats.dfMax));
   14053           5 :             CPLCreateXMLElementAndValue(
   14054           5 :                 psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
   14055           5 :             CPLCreateXMLElementAndValue(
   14056             :                 psMDArray, "StdDev",
   14057           5 :                 CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
   14058           5 :             CPLCreateXMLElementAndValue(
   14059             :                 psMDArray, "ValidSampleCount",
   14060           5 :                 CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
   14061             :         }
   14062             :     }
   14063             : 
   14064          58 :     std::vector<CPLErrorHandlerAccumulatorStruct> aoErrors;
   14065          29 :     CPLInstallErrorHandlerAccumulator(aoErrors);
   14066             :     const int bSaved =
   14067          29 :         CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
   14068          29 :     CPLUninstallErrorHandlerAccumulator();
   14069             : 
   14070          29 :     const char *pszNewPam = nullptr;
   14071          29 :     if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
   14072           0 :         ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
   14073             :     {
   14074           0 :         CPLErrorReset();
   14075           0 :         CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
   14076             :     }
   14077             :     else
   14078             :     {
   14079          29 :         for (const auto &oError : aoErrors)
   14080             :         {
   14081           0 :             CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
   14082             :         }
   14083             :     }
   14084          29 : }
   14085             : 
   14086             : /************************************************************************/
   14087             : /*                    GDALPamMultiDim::GetSpatialRef()                  */
   14088             : /************************************************************************/
   14089             : 
   14090             : std::shared_ptr<OGRSpatialReference>
   14091          10 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
   14092             :                                const std::string &osContext)
   14093             : {
   14094          10 :     Load();
   14095             :     auto oIter =
   14096          10 :         d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
   14097          10 :     if (oIter != d->m_oMapArray.end())
   14098           2 :         return oIter->second.poSRS;
   14099           8 :     return nullptr;
   14100             : }
   14101             : 
   14102             : /************************************************************************/
   14103             : /*                    GDALPamMultiDim::SetSpatialRef()                  */
   14104             : /************************************************************************/
   14105             : 
   14106          72 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
   14107             :                                     const std::string &osContext,
   14108             :                                     const OGRSpatialReference *poSRS)
   14109             : {
   14110          72 :     Load();
   14111          72 :     d->m_bDirty = true;
   14112          72 :     if (poSRS && !poSRS->IsEmpty())
   14113          71 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
   14114             :             poSRS->Clone());
   14115             :     else
   14116           2 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
   14117           1 :             .poSRS.reset();
   14118          72 : }
   14119             : 
   14120             : /************************************************************************/
   14121             : /*                           GetStatistics()                            */
   14122             : /************************************************************************/
   14123             : 
   14124          13 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
   14125             :                                       const std::string &osContext,
   14126             :                                       bool bApproxOK, double *pdfMin,
   14127             :                                       double *pdfMax, double *pdfMean,
   14128             :                                       double *pdfStdDev, GUInt64 *pnValidCount)
   14129             : {
   14130          13 :     Load();
   14131             :     auto oIter =
   14132          13 :         d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
   14133          13 :     if (oIter == d->m_oMapArray.end())
   14134           6 :         return CE_Failure;
   14135           7 :     const auto &stats = oIter->second.stats;
   14136           7 :     if (!stats.bHasStats)
   14137           1 :         return CE_Failure;
   14138           6 :     if (!bApproxOK && stats.bApproxStats)
   14139           0 :         return CE_Failure;
   14140           6 :     if (pdfMin)
   14141           6 :         *pdfMin = stats.dfMin;
   14142           6 :     if (pdfMax)
   14143           6 :         *pdfMax = stats.dfMax;
   14144           6 :     if (pdfMean)
   14145           6 :         *pdfMean = stats.dfMean;
   14146           6 :     if (pdfStdDev)
   14147           6 :         *pdfStdDev = stats.dfStdDev;
   14148           6 :     if (pnValidCount)
   14149           6 :         *pnValidCount = stats.nValidCount;
   14150           6 :     return CE_None;
   14151             : }
   14152             : 
   14153             : /************************************************************************/
   14154             : /*                           SetStatistics()                            */
   14155             : /************************************************************************/
   14156             : 
   14157           5 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
   14158             :                                     const std::string &osContext,
   14159             :                                     bool bApproxStats, double dfMin,
   14160             :                                     double dfMax, double dfMean,
   14161             :                                     double dfStdDev, GUInt64 nValidCount)
   14162             : {
   14163           5 :     Load();
   14164           5 :     d->m_bDirty = true;
   14165             :     auto &stats =
   14166           5 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
   14167           5 :     stats.bHasStats = true;
   14168           5 :     stats.bApproxStats = bApproxStats;
   14169           5 :     stats.dfMin = dfMin;
   14170           5 :     stats.dfMax = dfMax;
   14171           5 :     stats.dfMean = dfMean;
   14172           5 :     stats.dfStdDev = dfStdDev;
   14173           5 :     stats.nValidCount = nValidCount;
   14174           5 : }
   14175             : 
   14176             : /************************************************************************/
   14177             : /*                           ClearStatistics()                          */
   14178             : /************************************************************************/
   14179             : 
   14180           0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
   14181             :                                       const std::string &osContext)
   14182             : {
   14183           0 :     Load();
   14184           0 :     d->m_bDirty = true;
   14185           0 :     d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
   14186             :         false;
   14187           0 : }
   14188             : 
   14189             : /************************************************************************/
   14190             : /*                           ClearStatistics()                          */
   14191             : /************************************************************************/
   14192             : 
   14193           1 : void GDALPamMultiDim::ClearStatistics()
   14194             : {
   14195           1 :     Load();
   14196           1 :     d->m_bDirty = true;
   14197           3 :     for (auto &kv : d->m_oMapArray)
   14198           2 :         kv.second.stats.bHasStats = false;
   14199           1 : }
   14200             : 
   14201             : /************************************************************************/
   14202             : /*                             GetPAM()                                 */
   14203             : /************************************************************************/
   14204             : 
   14205             : /*static*/ std::shared_ptr<GDALPamMultiDim>
   14206         784 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
   14207             : {
   14208         784 :     auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
   14209         784 :     if (poPamArray)
   14210         563 :         return poPamArray->GetPAM();
   14211         221 :     return nullptr;
   14212             : }
   14213             : 
   14214             : /************************************************************************/
   14215             : /*                           GDALPamMDArray                             */
   14216             : /************************************************************************/
   14217             : 
   14218        3629 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
   14219             :                                const std::string &osName,
   14220             :                                const std::shared_ptr<GDALPamMultiDim> &poPam,
   14221           0 :                                const std::string &osContext)
   14222             :     :
   14223             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
   14224             :       GDALAbstractMDArray(osParentName, osName),
   14225             : #endif
   14226        3629 :       GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
   14227             : {
   14228        3629 : }
   14229             : 
   14230             : /************************************************************************/
   14231             : /*                    GDALPamMDArray::SetSpatialRef()                   */
   14232             : /************************************************************************/
   14233             : 
   14234          72 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
   14235             : {
   14236          72 :     if (!m_poPam)
   14237           0 :         return false;
   14238          72 :     m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
   14239          72 :     return true;
   14240             : }
   14241             : 
   14242             : /************************************************************************/
   14243             : /*                    GDALPamMDArray::GetSpatialRef()                   */
   14244             : /************************************************************************/
   14245             : 
   14246          10 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
   14247             : {
   14248          10 :     if (!m_poPam)
   14249           0 :         return nullptr;
   14250          10 :     return m_poPam->GetSpatialRef(GetFullName(), GetContext());
   14251             : }
   14252             : 
   14253             : /************************************************************************/
   14254             : /*                           GetStatistics()                            */
   14255             : /************************************************************************/
   14256             : 
   14257          13 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
   14258             :                                      double *pdfMin, double *pdfMax,
   14259             :                                      double *pdfMean, double *pdfStdDev,
   14260             :                                      GUInt64 *pnValidCount,
   14261             :                                      GDALProgressFunc pfnProgress,
   14262             :                                      void *pProgressData)
   14263             : {
   14264          13 :     if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
   14265             :                                           bApproxOK, pdfMin, pdfMax, pdfMean,
   14266          13 :                                           pdfStdDev, pnValidCount) == CE_None)
   14267             :     {
   14268           6 :         return CE_None;
   14269             :     }
   14270           7 :     if (!bForce)
   14271           4 :         return CE_Warning;
   14272             : 
   14273           3 :     return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
   14274             :                                       pdfMean, pdfStdDev, pnValidCount,
   14275           3 :                                       pfnProgress, pProgressData);
   14276             : }
   14277             : 
   14278             : /************************************************************************/
   14279             : /*                           SetStatistics()                            */
   14280             : /************************************************************************/
   14281             : 
   14282           5 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
   14283             :                                    double dfMax, double dfMean, double dfStdDev,
   14284             :                                    GUInt64 nValidCount,
   14285             :                                    CSLConstList /* papszOptions */)
   14286             : {
   14287           5 :     if (!m_poPam)
   14288           0 :         return false;
   14289           5 :     m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
   14290             :                            dfMax, dfMean, dfStdDev, nValidCount);
   14291           5 :     return true;
   14292             : }
   14293             : 
   14294             : /************************************************************************/
   14295             : /*                           ClearStatistics()                          */
   14296             : /************************************************************************/
   14297             : 
   14298           0 : void GDALPamMDArray::ClearStatistics()
   14299             : {
   14300           0 :     if (!m_poPam)
   14301           0 :         return;
   14302           0 :     m_poPam->ClearStatistics(GetFullName(), GetContext());
   14303             : }
   14304             : 
   14305             : //! @endcond

Generated by: LCOV version 1.14