LCOV - code coverage report
Current view: top level - gcore - gdalmultidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 4692 5203 90.2 %
Date: 2025-03-25 20:12:57 Functions: 462 537 86.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Name:     gdalmultidim.cpp
       4             :  * Project:  GDAL Core
       5             :  * Purpose:  GDAL Core C++/Private implementation for multidimensional support
       6             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include <assert.h>
      15             : #include <algorithm>
      16             : #include <limits>
      17             : #include <queue>
      18             : #include <set>
      19             : #include <utility>
      20             : #include <time.h>
      21             : 
      22             : #include <cmath>
      23             : #include <ctype.h>  // isalnum
      24             : 
      25             : #include "cpl_error_internal.h"
      26             : #include "cpl_float.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 :         GDALCopyWords64(
      71          13 :             &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
      72             :             eNonComplexDT, 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        6680 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
     334        6680 :                      const std::string &osContext)
     335        6680 :     : m_osName(osParentName.empty() ? "/" : osName),
     336             :       m_osFullName(
     337       13360 :           !osParentName.empty()
     338       10332 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
     339             :               : "/"),
     340       17012 :       m_osContext(osContext)
     341             : {
     342        6680 : }
     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           4 :                 (srcArrayType.GetNumericDataType() == GDT_Float16 ||
     985           2 :                  srcArrayType.GetNumericDataType() == GDT_Float32 ||
     986           0 :                  srcArrayType.GetNumericDataType() == GDT_Float64) &&
     987           2 :                 srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
     988          43 :                 srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
     989          41 :                 oIterDimName == mapSrcVariableNameToIndexedDimName.end())
     990             :             {
     991           2 :                 constexpr bool bApproxOK = false;
     992           2 :                 constexpr bool bForce = true;
     993           2 :                 double dfMin = 0.0;
     994           2 :                 double dfMax = 0.0;
     995           2 :                 if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
     996             :                                             nullptr, nullptr, nullptr, nullptr,
     997           2 :                                             nullptr) != CE_None)
     998             :                 {
     999           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1000             :                              "Could not retrieve statistics for array %s",
    1001           0 :                              srcArray->GetName().c_str());
    1002           0 :                     return false;
    1003             :                 }
    1004           2 :                 double dfDTMin = 0;
    1005           2 :                 double dfDTMax = 0;
    1006             : #define setDTMinMax(ctype)                                                     \
    1007             :     do                                                                         \
    1008             :     {                                                                          \
    1009             :         dfDTMin = static_cast<double>(cpl::NumericLimits<ctype>::lowest());    \
    1010             :         dfDTMax = static_cast<double>(cpl::NumericLimits<ctype>::max());       \
    1011             :     } while (0)
    1012             : 
    1013           2 :                 switch (eAutoScaleType)
    1014             :                 {
    1015           0 :                     case GDT_Byte:
    1016           0 :                         setDTMinMax(GByte);
    1017           0 :                         break;
    1018           0 :                     case GDT_Int8:
    1019           0 :                         setDTMinMax(GInt8);
    1020           0 :                         break;
    1021           1 :                     case GDT_UInt16:
    1022           1 :                         setDTMinMax(GUInt16);
    1023           1 :                         break;
    1024           1 :                     case GDT_Int16:
    1025           1 :                         setDTMinMax(GInt16);
    1026           1 :                         break;
    1027           0 :                     case GDT_UInt32:
    1028           0 :                         setDTMinMax(GUInt32);
    1029           0 :                         break;
    1030           0 :                     case GDT_Int32:
    1031           0 :                         setDTMinMax(GInt32);
    1032           0 :                         break;
    1033           0 :                     case GDT_UInt64:
    1034           0 :                         setDTMinMax(std::uint64_t);
    1035           0 :                         break;
    1036           0 :                     case GDT_Int64:
    1037           0 :                         setDTMinMax(std::int64_t);
    1038           0 :                         break;
    1039           0 :                     case GDT_Float16:
    1040             :                     case GDT_Float32:
    1041             :                     case GDT_Float64:
    1042             :                     case GDT_Unknown:
    1043             :                     case GDT_CInt16:
    1044             :                     case GDT_CInt32:
    1045             :                     case GDT_CFloat16:
    1046             :                     case GDT_CFloat32:
    1047             :                     case GDT_CFloat64:
    1048             :                     case GDT_TypeCount:
    1049           0 :                         CPLAssert(false);
    1050             :                 }
    1051             : 
    1052             :                 dstArray =
    1053           4 :                     CreateMDArray(srcArray->GetName(), dstArrayDims,
    1054           4 :                                   GDALExtendedDataType::Create(eAutoScaleType),
    1055           4 :                                   aosArrayCO.List());
    1056           2 :                 if (!dstArray)
    1057           0 :                     return !bStrict;
    1058             : 
    1059           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1060             :                 {
    1061             :                     // If there's a nodata value in the source array, reserve
    1062             :                     // DTMax for that purpose in the target scaled array
    1063           1 :                     if (!dstArray->SetNoDataValue(dfDTMax))
    1064             :                     {
    1065           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    1066             :                                  "Cannot set nodata value");
    1067           0 :                         return false;
    1068             :                     }
    1069           1 :                     dfDTMax--;
    1070             :                 }
    1071           2 :                 const double dfScale =
    1072           2 :                     dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
    1073           2 :                 const double dfOffset = dfMin - dfDTMin * dfScale;
    1074             : 
    1075           4 :                 if (!dstArray->SetOffset(dfOffset) ||
    1076           2 :                     !dstArray->SetScale(dfScale))
    1077             :                 {
    1078           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1079             :                              "Cannot set scale/offset");
    1080           0 :                     return false;
    1081             :                 }
    1082             : 
    1083           2 :                 auto poUnscaled = dstArray->GetUnscaled();
    1084           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1085             :                 {
    1086           1 :                     poUnscaled->SetNoDataValue(
    1087             :                         srcArray->GetNoDataValueAsDouble());
    1088             :                 }
    1089             : 
    1090             :                 // Copy source array into unscaled array
    1091           4 :                 if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1092             :                                           nCurCost, nTotalCost, pfnProgress,
    1093           2 :                                           pProgressData))
    1094             :                 {
    1095           0 :                     return false;
    1096             :                 }
    1097             :             }
    1098             :             else
    1099             :             {
    1100          74 :                 dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
    1101          74 :                                          srcArrayType, aosArrayCO.List());
    1102          37 :                 if (!dstArray)
    1103           0 :                     return !bStrict;
    1104             : 
    1105          74 :                 if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1106             :                                         nCurCost, nTotalCost, pfnProgress,
    1107          37 :                                         pProgressData))
    1108             :                 {
    1109           0 :                     return false;
    1110             :                 }
    1111             :             }
    1112             : 
    1113             :             // If this array is the indexing variable of a dimension, link them
    1114             :             // together.
    1115          39 :             if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
    1116             :             {
    1117             :                 auto oCorrespondingDimIter =
    1118          16 :                     mapExistingDstDims.find(oIterDimName->second);
    1119          16 :                 if (oCorrespondingDimIter != mapExistingDstDims.end())
    1120             :                 {
    1121             :                     CPLErrorStateBackuper oErrorStateBackuper(
    1122          16 :                         CPLQuietErrorHandler);
    1123          32 :                     oCorrespondingDimIter->second->SetIndexingVariable(
    1124          16 :                         std::move(dstArray));
    1125             :                 }
    1126             :             }
    1127             : 
    1128          39 :             return true;
    1129          22 :         };
    1130             : 
    1131          44 :         const auto arrayNames = poSrcGroup->GetMDArrayNames();
    1132             : 
    1133             :         // Start by copying arrays that are indexing variables of dimensions
    1134          61 :         for (const auto &name : arrayNames)
    1135             :         {
    1136          39 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1137          39 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1138             : 
    1139          39 :             if (cpl::contains(mapSrcVariableNameToIndexedDimName,
    1140          39 :                               srcArray->GetName()))
    1141             :             {
    1142          16 :                 if (!CopyArray(srcArray))
    1143           0 :                     return false;
    1144             :             }
    1145             :         }
    1146             : 
    1147             :         // Then copy regular arrays
    1148          61 :         for (const auto &name : arrayNames)
    1149             :         {
    1150          39 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1151          39 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1152             : 
    1153          39 :             if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
    1154          39 :                                srcArray->GetName()))
    1155             :             {
    1156          23 :                 if (!CopyArray(srcArray))
    1157           0 :                     return false;
    1158             :             }
    1159             :         }
    1160             : 
    1161          44 :         const auto groupNames = poSrcGroup->GetGroupNames();
    1162          26 :         for (const auto &name : groupNames)
    1163             :         {
    1164           4 :             auto srcSubGroup = poSrcGroup->OpenGroup(name);
    1165           4 :             EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
    1166           4 :             auto dstSubGroup = CreateGroup(name);
    1167           4 :             EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
    1168           8 :             if (!dstSubGroup->CopyFrom(
    1169             :                     poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
    1170           4 :                     nTotalCost, pfnProgress, pProgressData, papszOptions))
    1171           0 :                 return false;
    1172             :         }
    1173             : 
    1174          22 :         if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    1175           0 :             return false;
    1176             : 
    1177          22 :         return true;
    1178             :     }
    1179           0 :     catch (const std::exception &e)
    1180             :     {
    1181           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1182           0 :         return false;
    1183             :     }
    1184             : }
    1185             : 
    1186             : /************************************************************************/
    1187             : /*                         GetInnerMostGroup()                          */
    1188             : /************************************************************************/
    1189             : 
    1190             : //! @cond Doxygen_Suppress
    1191             : const GDALGroup *
    1192        1181 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
    1193             :                              std::shared_ptr<GDALGroup> &curGroupHolder,
    1194             :                              std::string &osLastPart) const
    1195             : {
    1196        1181 :     if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
    1197           6 :         return nullptr;
    1198        1175 :     const GDALGroup *poCurGroup = this;
    1199             :     CPLStringList aosTokens(
    1200        2350 :         CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
    1201        1175 :     if (aosTokens.size() == 0)
    1202             :     {
    1203           0 :         return nullptr;
    1204             :     }
    1205             : 
    1206        1512 :     for (int i = 0; i < aosTokens.size() - 1; i++)
    1207             :     {
    1208         345 :         curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
    1209         345 :         if (!curGroupHolder)
    1210             :         {
    1211           8 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
    1212             :                      aosTokens[i]);
    1213           8 :             return nullptr;
    1214             :         }
    1215         337 :         poCurGroup = curGroupHolder.get();
    1216             :     }
    1217        1167 :     osLastPart = aosTokens[aosTokens.size() - 1];
    1218        1167 :     return poCurGroup;
    1219             : }
    1220             : 
    1221             : //! @endcond
    1222             : 
    1223             : /************************************************************************/
    1224             : /*                      OpenMDArrayFromFullname()                       */
    1225             : /************************************************************************/
    1226             : 
    1227             : /** Get an array from its fully qualified name */
    1228             : std::shared_ptr<GDALMDArray>
    1229         487 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
    1230             :                                    CSLConstList papszOptions) const
    1231             : {
    1232         974 :     std::string osName;
    1233         487 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1234         487 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1235         487 :     if (poGroup == nullptr)
    1236          10 :         return nullptr;
    1237         477 :     return poGroup->OpenMDArray(osName, papszOptions);
    1238             : }
    1239             : 
    1240             : /************************************************************************/
    1241             : /*                          ResolveMDArray()                            */
    1242             : /************************************************************************/
    1243             : 
    1244             : /** Locate an array in a group and its subgroups by name.
    1245             :  *
    1246             :  * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
    1247             :  * used
    1248             :  * Otherwise the search will start from the group identified by osStartingPath,
    1249             :  * and an array whose name is osName will be looked for in this group (if
    1250             :  * osStartingPath is empty or "/", then the current group is used). If there
    1251             :  * is no match, then a recursive descendent search will be made in its
    1252             :  * subgroups. If there is no match in the subgroups, then the parent (if
    1253             :  * existing) of the group pointed by osStartingPath will be used as the new
    1254             :  * starting point for the search.
    1255             :  *
    1256             :  * @param osName name, qualified or not
    1257             :  * @param osStartingPath fully qualified name of the (sub-)group from which
    1258             :  *                       the search should be started. If this is a non-empty
    1259             :  *                       string, the group on which this method is called should
    1260             :  *                       nominally be the root group (otherwise the path will
    1261             :  *                       be interpreted as from the current group)
    1262             :  * @param papszOptions options to pass to OpenMDArray()
    1263             :  * @since GDAL 3.2
    1264             :  */
    1265             : std::shared_ptr<GDALMDArray>
    1266          19 : GDALGroup::ResolveMDArray(const std::string &osName,
    1267             :                           const std::string &osStartingPath,
    1268             :                           CSLConstList papszOptions) const
    1269             : {
    1270          19 :     if (!osName.empty() && osName[0] == '/')
    1271             :     {
    1272           1 :         auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
    1273           1 :         if (poArray)
    1274           1 :             return poArray;
    1275             :     }
    1276          36 :     std::string osPath(osStartingPath);
    1277          36 :     std::set<std::string> oSetAlreadyVisited;
    1278             : 
    1279             :     while (true)
    1280             :     {
    1281           0 :         std::shared_ptr<GDALGroup> curGroupHolder;
    1282           0 :         std::shared_ptr<GDALGroup> poGroup;
    1283             : 
    1284          22 :         std::queue<std::shared_ptr<GDALGroup>> oQueue;
    1285          22 :         bool goOn = false;
    1286          22 :         if (osPath.empty() || osPath == "/")
    1287             :         {
    1288          11 :             goOn = true;
    1289             :         }
    1290             :         else
    1291             :         {
    1292          22 :             std::string osLastPart;
    1293             :             const GDALGroup *poGroupPtr =
    1294          11 :                 GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
    1295          11 :             if (poGroupPtr)
    1296          11 :                 poGroup = poGroupPtr->OpenGroup(osLastPart);
    1297          22 :             if (poGroup &&
    1298          22 :                 !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
    1299             :             {
    1300          11 :                 oQueue.push(poGroup);
    1301          11 :                 goOn = true;
    1302             :             }
    1303             :         }
    1304             : 
    1305          22 :         if (goOn)
    1306             :         {
    1307          17 :             do
    1308             :             {
    1309             :                 const GDALGroup *groupPtr;
    1310          39 :                 if (!oQueue.empty())
    1311             :                 {
    1312          28 :                     poGroup = oQueue.front();
    1313          28 :                     oQueue.pop();
    1314          28 :                     groupPtr = poGroup.get();
    1315             :                 }
    1316             :                 else
    1317             :                 {
    1318          11 :                     groupPtr = this;
    1319             :                 }
    1320             : 
    1321          39 :                 auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
    1322          39 :                 if (poArray)
    1323          16 :                     return poArray;
    1324             : 
    1325          46 :                 const auto aosGroupNames = groupPtr->GetGroupNames();
    1326          47 :                 for (const auto &osGroupName : aosGroupNames)
    1327             :                 {
    1328          48 :                     auto poSubGroup = groupPtr->OpenGroup(osGroupName);
    1329          48 :                     if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
    1330          48 :                                                      poSubGroup->GetFullName()))
    1331             :                     {
    1332          24 :                         oQueue.push(poSubGroup);
    1333          24 :                         oSetAlreadyVisited.insert(poSubGroup->GetFullName());
    1334             :                     }
    1335             :                 }
    1336          23 :             } while (!oQueue.empty());
    1337             :         }
    1338             : 
    1339           6 :         if (osPath.empty() || osPath == "/")
    1340           2 :             break;
    1341             : 
    1342           4 :         const auto nPos = osPath.rfind('/');
    1343           4 :         if (nPos == 0)
    1344           1 :             osPath = "/";
    1345             :         else
    1346             :         {
    1347           3 :             if (nPos == std::string::npos)
    1348           0 :                 break;
    1349           3 :             osPath.resize(nPos);
    1350             :         }
    1351           4 :     }
    1352           2 :     return nullptr;
    1353             : }
    1354             : 
    1355             : /************************************************************************/
    1356             : /*                       OpenGroupFromFullname()                        */
    1357             : /************************************************************************/
    1358             : 
    1359             : /** Get a group from its fully qualified name.
    1360             :  * @since GDAL 3.2
    1361             :  */
    1362             : std::shared_ptr<GDALGroup>
    1363         558 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
    1364             :                                  CSLConstList papszOptions) const
    1365             : {
    1366        1116 :     std::string osName;
    1367         558 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1368         558 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1369         558 :     if (poGroup == nullptr)
    1370           2 :         return nullptr;
    1371         556 :     return poGroup->OpenGroup(osName, papszOptions);
    1372             : }
    1373             : 
    1374             : /************************************************************************/
    1375             : /*                      OpenDimensionFromFullname()                     */
    1376             : /************************************************************************/
    1377             : 
    1378             : /** Get a dimension from its fully qualified name */
    1379             : std::shared_ptr<GDALDimension>
    1380         125 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
    1381             : {
    1382         250 :     std::string osName;
    1383         125 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1384         125 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1385         125 :     if (poGroup == nullptr)
    1386           2 :         return nullptr;
    1387         246 :     auto dims(poGroup->GetDimensions());
    1388         203 :     for (auto &dim : dims)
    1389             :     {
    1390         164 :         if (dim->GetName() == osName)
    1391          84 :             return dim;
    1392             :     }
    1393          39 :     return nullptr;
    1394             : }
    1395             : 
    1396             : /************************************************************************/
    1397             : /*                           ClearStatistics()                          */
    1398             : /************************************************************************/
    1399             : 
    1400             : /**
    1401             :  * \brief Clear statistics.
    1402             :  *
    1403             :  * @since GDAL 3.4
    1404             :  */
    1405           0 : void GDALGroup::ClearStatistics()
    1406             : {
    1407           0 :     auto groupNames = GetGroupNames();
    1408           0 :     for (const auto &name : groupNames)
    1409             :     {
    1410           0 :         auto subGroup = OpenGroup(name);
    1411           0 :         if (subGroup)
    1412             :         {
    1413           0 :             subGroup->ClearStatistics();
    1414             :         }
    1415             :     }
    1416             : 
    1417           0 :     auto arrayNames = GetMDArrayNames();
    1418           0 :     for (const auto &name : arrayNames)
    1419             :     {
    1420           0 :         auto array = OpenMDArray(name);
    1421           0 :         if (array)
    1422             :         {
    1423           0 :             array->ClearStatistics();
    1424             :         }
    1425             :     }
    1426           0 : }
    1427             : 
    1428             : /************************************************************************/
    1429             : /*                            Rename()                                  */
    1430             : /************************************************************************/
    1431             : 
    1432             : /** Rename the group.
    1433             :  *
    1434             :  * This is not implemented by all drivers.
    1435             :  *
    1436             :  * Drivers known to implement it: MEM, netCDF, ZARR.
    1437             :  *
    1438             :  * This is the same as the C function GDALGroupRename().
    1439             :  *
    1440             :  * @param osNewName New name.
    1441             :  *
    1442             :  * @return true in case of success
    1443             :  * @since GDAL 3.8
    1444             :  */
    1445           0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
    1446             : {
    1447           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
    1448           0 :     return false;
    1449             : }
    1450             : 
    1451             : /************************************************************************/
    1452             : /*                         BaseRename()                                 */
    1453             : /************************************************************************/
    1454             : 
    1455             : //! @cond Doxygen_Suppress
    1456           8 : void GDALGroup::BaseRename(const std::string &osNewName)
    1457             : {
    1458           8 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
    1459           8 :     m_osFullName += osNewName;
    1460           8 :     m_osName = osNewName;
    1461             : 
    1462           8 :     NotifyChildrenOfRenaming();
    1463           8 : }
    1464             : 
    1465             : //! @endcond
    1466             : 
    1467             : /************************************************************************/
    1468             : /*                        ParentRenamed()                               */
    1469             : /************************************************************************/
    1470             : 
    1471             : //! @cond Doxygen_Suppress
    1472           7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
    1473             : {
    1474           7 :     m_osFullName = osNewParentFullName;
    1475           7 :     m_osFullName += "/";
    1476           7 :     m_osFullName += m_osName;
    1477             : 
    1478           7 :     NotifyChildrenOfRenaming();
    1479           7 : }
    1480             : 
    1481             : //! @endcond
    1482             : 
    1483             : /************************************************************************/
    1484             : /*                             Deleted()                                */
    1485             : /************************************************************************/
    1486             : 
    1487             : //! @cond Doxygen_Suppress
    1488          22 : void GDALGroup::Deleted()
    1489             : {
    1490          22 :     m_bValid = false;
    1491             : 
    1492          22 :     NotifyChildrenOfDeletion();
    1493          22 : }
    1494             : 
    1495             : //! @endcond
    1496             : 
    1497             : /************************************************************************/
    1498             : /*                        ParentDeleted()                               */
    1499             : /************************************************************************/
    1500             : 
    1501             : //! @cond Doxygen_Suppress
    1502           3 : void GDALGroup::ParentDeleted()
    1503             : {
    1504           3 :     Deleted();
    1505           3 : }
    1506             : 
    1507             : //! @endcond
    1508             : 
    1509             : /************************************************************************/
    1510             : /*                     CheckValidAndErrorOutIfNot()                     */
    1511             : /************************************************************************/
    1512             : 
    1513             : //! @cond Doxygen_Suppress
    1514       12173 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
    1515             : {
    1516       12173 :     if (!m_bValid)
    1517             :     {
    1518          14 :         CPLError(CE_Failure, CPLE_AppDefined,
    1519             :                  "This object has been deleted. No action on it is possible");
    1520             :     }
    1521       12173 :     return m_bValid;
    1522             : }
    1523             : 
    1524             : //! @endcond
    1525             : 
    1526             : /************************************************************************/
    1527             : /*                       ~GDALAbstractMDArray()                         */
    1528             : /************************************************************************/
    1529             : 
    1530             : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
    1531             : 
    1532             : /************************************************************************/
    1533             : /*                        GDALAbstractMDArray()                         */
    1534             : /************************************************************************/
    1535             : 
    1536             : //! @cond Doxygen_Suppress
    1537       20254 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
    1538       20254 :                                          const std::string &osName)
    1539             :     : m_osName(osName),
    1540             :       m_osFullName(
    1541       20254 :           !osParentName.empty()
    1542       38869 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
    1543       79377 :               : osName)
    1544             : {
    1545       20254 : }
    1546             : 
    1547             : //! @endcond
    1548             : 
    1549             : /************************************************************************/
    1550             : /*                           GetDimensions()                            */
    1551             : /************************************************************************/
    1552             : 
    1553             : /** \fn GDALAbstractMDArray::GetDimensions() const
    1554             :  * \brief Return the dimensions of an attribute/array.
    1555             :  *
    1556             :  * This is the same as the C functions GDALMDArrayGetDimensions() and
    1557             :  * similar to GDALAttributeGetDimensionsSize().
    1558             :  */
    1559             : 
    1560             : /************************************************************************/
    1561             : /*                           GetDataType()                              */
    1562             : /************************************************************************/
    1563             : 
    1564             : /** \fn GDALAbstractMDArray::GetDataType() const
    1565             :  * \brief Return the data type of an attribute/array.
    1566             :  *
    1567             :  * This is the same as the C functions GDALMDArrayGetDataType() and
    1568             :  * GDALAttributeGetDataType()
    1569             :  */
    1570             : 
    1571             : /************************************************************************/
    1572             : /*                        GetDimensionCount()                           */
    1573             : /************************************************************************/
    1574             : 
    1575             : /** Return the number of dimensions.
    1576             :  *
    1577             :  * Default implementation is GetDimensions().size(), and may be overridden by
    1578             :  * drivers if they have a faster / less expensive implementations.
    1579             :  *
    1580             :  * This is the same as the C function GDALMDArrayGetDimensionCount() or
    1581             :  * GDALAttributeGetDimensionCount().
    1582             :  *
    1583             :  */
    1584       22593 : size_t GDALAbstractMDArray::GetDimensionCount() const
    1585             : {
    1586       22593 :     return GetDimensions().size();
    1587             : }
    1588             : 
    1589             : /************************************************************************/
    1590             : /*                            Rename()                                  */
    1591             : /************************************************************************/
    1592             : 
    1593             : /** Rename the attribute/array.
    1594             :  *
    1595             :  * This is not implemented by all drivers.
    1596             :  *
    1597             :  * Drivers known to implement it: MEM, netCDF, Zarr.
    1598             :  *
    1599             :  * This is the same as the C functions GDALMDArrayRename() or
    1600             :  * GDALAttributeRename().
    1601             :  *
    1602             :  * @param osNewName New name.
    1603             :  *
    1604             :  * @return true in case of success
    1605             :  * @since GDAL 3.8
    1606             :  */
    1607           0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
    1608             : {
    1609           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
    1610           0 :     return false;
    1611             : }
    1612             : 
    1613             : /************************************************************************/
    1614             : /*                             CopyValue()                              */
    1615             : /************************************************************************/
    1616             : 
    1617             : /** Convert a value from a source type to a destination type.
    1618             :  *
    1619             :  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
    1620             :  * that must be freed with CPLFree().
    1621             :  */
    1622       78860 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
    1623             :                                      const GDALExtendedDataType &srcType,
    1624             :                                      void *pDst,
    1625             :                                      const GDALExtendedDataType &dstType)
    1626             : {
    1627      154409 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1628       75549 :         dstType.GetClass() == GEDTC_NUMERIC)
    1629             :     {
    1630       75342 :         GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
    1631             :                         dstType.GetNumericDataType(), 0, 1);
    1632       75342 :         return true;
    1633             :     }
    1634        6654 :     if (srcType.GetClass() == GEDTC_STRING &&
    1635        3136 :         dstType.GetClass() == GEDTC_STRING)
    1636             :     {
    1637             :         const char *srcStrPtr;
    1638        2748 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
    1639        2748 :         char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
    1640        2748 :         *reinterpret_cast<void **>(pDst) = pszDup;
    1641        2748 :         return true;
    1642             :     }
    1643         977 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1644         207 :         dstType.GetClass() == GEDTC_STRING)
    1645             :     {
    1646         207 :         const char *str = nullptr;
    1647         207 :         switch (srcType.GetNumericDataType())
    1648             :         {
    1649           0 :             case GDT_Unknown:
    1650           0 :                 break;
    1651           0 :             case GDT_Byte:
    1652           0 :                 str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
    1653           0 :                 break;
    1654           3 :             case GDT_Int8:
    1655           3 :                 str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
    1656           3 :                 break;
    1657          48 :             case GDT_UInt16:
    1658          48 :                 str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
    1659          48 :                 break;
    1660           0 :             case GDT_Int16:
    1661           0 :                 str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
    1662           0 :                 break;
    1663           8 :             case GDT_UInt32:
    1664           8 :                 str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
    1665           8 :                 break;
    1666          54 :             case GDT_Int32:
    1667          54 :                 str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
    1668          54 :                 break;
    1669           0 :             case GDT_UInt64:
    1670             :                 str =
    1671           0 :                     CPLSPrintf(CPL_FRMT_GUIB,
    1672             :                                static_cast<GUIntBig>(
    1673             :                                    *static_cast<const std::uint64_t *>(pSrc)));
    1674           0 :                 break;
    1675           0 :             case GDT_Int64:
    1676           0 :                 str = CPLSPrintf(CPL_FRMT_GIB,
    1677             :                                  static_cast<GIntBig>(
    1678             :                                      *static_cast<const std::int64_t *>(pSrc)));
    1679           0 :                 break;
    1680           0 :             case GDT_Float16:
    1681           0 :                 str = CPLSPrintf("%.5g",
    1682             :                                  double(*static_cast<const GFloat16 *>(pSrc)));
    1683           0 :                 break;
    1684          17 :             case GDT_Float32:
    1685          17 :                 str = CPLSPrintf("%.9g", *static_cast<const float *>(pSrc));
    1686          17 :                 break;
    1687          75 :             case GDT_Float64:
    1688          75 :                 str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
    1689          75 :                 break;
    1690           2 :             case GDT_CInt16:
    1691             :             {
    1692           2 :                 const GInt16 *src = static_cast<const GInt16 *>(pSrc);
    1693           2 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
    1694           2 :                 break;
    1695             :             }
    1696           0 :             case GDT_CInt32:
    1697             :             {
    1698           0 :                 const GInt32 *src = static_cast<const GInt32 *>(pSrc);
    1699           0 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
    1700           0 :                 break;
    1701             :             }
    1702           0 :             case GDT_CFloat16:
    1703             :             {
    1704           0 :                 const GFloat16 *src = static_cast<const GFloat16 *>(pSrc);
    1705           0 :                 str = CPLSPrintf("%.5g+%.5gj", double(src[0]), double(src[1]));
    1706           0 :                 break;
    1707             :             }
    1708           0 :             case GDT_CFloat32:
    1709             :             {
    1710           0 :                 const float *src = static_cast<const float *>(pSrc);
    1711           0 :                 str = CPLSPrintf("%.9g+%.9gj", src[0], src[1]);
    1712           0 :                 break;
    1713             :             }
    1714           0 :             case GDT_CFloat64:
    1715             :             {
    1716           0 :                 const double *src = static_cast<const double *>(pSrc);
    1717           0 :                 str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
    1718           0 :                 break;
    1719             :             }
    1720           0 :             case GDT_TypeCount:
    1721           0 :                 CPLAssert(false);
    1722             :                 break;
    1723             :         }
    1724         207 :         char *pszDup = str ? CPLStrdup(str) : nullptr;
    1725         207 :         *reinterpret_cast<void **>(pDst) = pszDup;
    1726         207 :         return true;
    1727             :     }
    1728         951 :     if (srcType.GetClass() == GEDTC_STRING &&
    1729         388 :         dstType.GetClass() == GEDTC_NUMERIC)
    1730             :     {
    1731             :         const char *srcStrPtr;
    1732         388 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
    1733         388 :         if (dstType.GetNumericDataType() == GDT_Int64)
    1734             :         {
    1735           2 :             *(static_cast<int64_t *>(pDst)) =
    1736           2 :                 srcStrPtr == nullptr ? 0
    1737           1 :                                      : static_cast<int64_t>(atoll(srcStrPtr));
    1738             :         }
    1739         386 :         else if (dstType.GetNumericDataType() == GDT_UInt64)
    1740             :         {
    1741           2 :             *(static_cast<uint64_t *>(pDst)) =
    1742           2 :                 srcStrPtr == nullptr
    1743           2 :                     ? 0
    1744           1 :                     : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
    1745             :         }
    1746             :         else
    1747             :         {
    1748             :             // FIXME GDT_UInt64
    1749         384 :             const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
    1750         384 :             GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
    1751             :                             dstType.GetNumericDataType(), 0, 1);
    1752             :         }
    1753         388 :         return true;
    1754             :     }
    1755         350 :     if (srcType.GetClass() == GEDTC_COMPOUND &&
    1756         175 :         dstType.GetClass() == GEDTC_COMPOUND)
    1757             :     {
    1758         175 :         const auto &srcComponents = srcType.GetComponents();
    1759         175 :         const auto &dstComponents = dstType.GetComponents();
    1760         175 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
    1761         175 :         GByte *pabyDst = static_cast<GByte *>(pDst);
    1762             : 
    1763             :         std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
    1764         350 :             srcComponentMap;
    1765         688 :         for (const auto &srcComp : srcComponents)
    1766             :         {
    1767         513 :             srcComponentMap[srcComp->GetName()] = &srcComp;
    1768             :         }
    1769         504 :         for (const auto &dstComp : dstComponents)
    1770             :         {
    1771         329 :             auto oIter = srcComponentMap.find(dstComp->GetName());
    1772         329 :             if (oIter == srcComponentMap.end())
    1773           0 :                 return false;
    1774         329 :             const auto &srcComp = *(oIter->second);
    1775         987 :             if (!GDALExtendedDataType::CopyValue(
    1776         329 :                     pabySrc + srcComp->GetOffset(), srcComp->GetType(),
    1777         329 :                     pabyDst + dstComp->GetOffset(), dstComp->GetType()))
    1778             :             {
    1779           0 :                 return false;
    1780             :             }
    1781             :         }
    1782         175 :         return true;
    1783             :     }
    1784             : 
    1785           0 :     return false;
    1786             : }
    1787             : 
    1788             : /************************************************************************/
    1789             : /*                             CopyValues()                             */
    1790             : /************************************************************************/
    1791             : 
    1792             : /** Convert severals value from a source type to a destination type.
    1793             :  *
    1794             :  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
    1795             :  * that must be freed with CPLFree().
    1796             :  */
    1797         328 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
    1798             :                                       const GDALExtendedDataType &srcType,
    1799             :                                       GPtrDiff_t nSrcStrideInElts, void *pDst,
    1800             :                                       const GDALExtendedDataType &dstType,
    1801             :                                       GPtrDiff_t nDstStrideInElts,
    1802             :                                       size_t nValues)
    1803             : {
    1804             :     const auto nSrcStrideInBytes =
    1805         328 :         nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
    1806             :     const auto nDstStrideInBytes =
    1807         328 :         nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
    1808         594 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1809         266 :         dstType.GetClass() == GEDTC_NUMERIC &&
    1810         266 :         nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
    1811         266 :         nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
    1812         860 :         nDstStrideInBytes >= std::numeric_limits<int>::min() &&
    1813         266 :         nDstStrideInBytes <= std::numeric_limits<int>::max())
    1814             :     {
    1815         266 :         GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
    1816             :                         static_cast<int>(nSrcStrideInBytes), pDst,
    1817             :                         dstType.GetNumericDataType(),
    1818             :                         static_cast<int>(nDstStrideInBytes), nValues);
    1819             :     }
    1820             :     else
    1821             :     {
    1822          62 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
    1823          62 :         GByte *pabyDst = static_cast<GByte *>(pDst);
    1824         124 :         for (size_t i = 0; i < nValues; ++i)
    1825             :         {
    1826          62 :             if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
    1827           0 :                 return false;
    1828          62 :             pabySrc += nSrcStrideInBytes;
    1829          62 :             pabyDst += nDstStrideInBytes;
    1830             :         }
    1831             :     }
    1832         328 :     return true;
    1833             : }
    1834             : 
    1835             : /************************************************************************/
    1836             : /*                       CheckReadWriteParams()                         */
    1837             : /************************************************************************/
    1838             : //! @cond Doxygen_Suppress
    1839        7897 : bool GDALAbstractMDArray::CheckReadWriteParams(
    1840             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
    1841             :     const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
    1842             :     const void *buffer, const void *buffer_alloc_start,
    1843             :     size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
    1844             :     std::vector<GPtrDiff_t> &tmp_bufferStride) const
    1845             : {
    1846           0 :     const auto lamda_error = []()
    1847             :     {
    1848           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1849             :                  "Not all elements pointed by buffer will fit in "
    1850             :                  "[buffer_alloc_start, "
    1851             :                  "buffer_alloc_start + buffer_alloc_size]");
    1852           0 :     };
    1853             : 
    1854        7897 :     const auto &dims = GetDimensions();
    1855        7897 :     if (dims.empty())
    1856             :     {
    1857        3041 :         if (buffer_alloc_start)
    1858             :         {
    1859        2668 :             const size_t elementSize = bufferDataType.GetSize();
    1860        2668 :             const GByte *paby_buffer = static_cast<const GByte *>(buffer);
    1861        2668 :             const GByte *paby_buffer_alloc_start =
    1862             :                 static_cast<const GByte *>(buffer_alloc_start);
    1863        2668 :             const GByte *paby_buffer_alloc_end =
    1864             :                 paby_buffer_alloc_start + buffer_alloc_size;
    1865             : 
    1866        2668 :             if (paby_buffer < paby_buffer_alloc_start ||
    1867        2668 :                 paby_buffer + elementSize > paby_buffer_alloc_end)
    1868             :             {
    1869           0 :                 lamda_error();
    1870           0 :                 return false;
    1871             :             }
    1872             :         }
    1873        3041 :         return true;
    1874             :     }
    1875             : 
    1876        4856 :     if (arrayStep == nullptr)
    1877             :     {
    1878        1273 :         tmp_arrayStep.resize(dims.size(), 1);
    1879        1273 :         arrayStep = tmp_arrayStep.data();
    1880             :     }
    1881       13741 :     for (size_t i = 0; i < dims.size(); i++)
    1882             :     {
    1883        8885 :         if (count[i] == 0)
    1884             :         {
    1885           0 :             CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
    1886             :                      static_cast<unsigned>(i));
    1887           0 :             return false;
    1888             :         }
    1889             :     }
    1890        4856 :     bool bufferStride_all_positive = true;
    1891        4856 :     if (bufferStride == nullptr)
    1892             :     {
    1893         985 :         GPtrDiff_t stride = 1;
    1894             :         // To compute strides we must proceed from the fastest varying dimension
    1895             :         // (the last one), and then reverse the result
    1896        2241 :         for (size_t i = dims.size(); i != 0;)
    1897             :         {
    1898        1256 :             --i;
    1899        1256 :             tmp_bufferStride.push_back(stride);
    1900        1256 :             GUInt64 newStride = 0;
    1901             :             bool bOK;
    1902             :             try
    1903             :             {
    1904        1256 :                 newStride = (CPLSM(static_cast<uint64_t>(stride)) *
    1905        2512 :                              CPLSM(static_cast<uint64_t>(count[i])))
    1906        1256 :                                 .v();
    1907        1256 :                 bOK = static_cast<size_t>(newStride) == newStride &&
    1908        1256 :                       newStride < std::numeric_limits<size_t>::max() / 2;
    1909             :             }
    1910           0 :             catch (...)
    1911             :             {
    1912           0 :                 bOK = false;
    1913             :             }
    1914        1256 :             if (!bOK)
    1915             :             {
    1916           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
    1917           0 :                 return false;
    1918             :             }
    1919        1256 :             stride = static_cast<GPtrDiff_t>(newStride);
    1920             :         }
    1921         985 :         std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
    1922         985 :         bufferStride = tmp_bufferStride.data();
    1923             :     }
    1924             :     else
    1925             :     {
    1926       11498 :         for (size_t i = 0; i < dims.size(); i++)
    1927             :         {
    1928        7628 :             if (bufferStride[i] < 0)
    1929             :             {
    1930           1 :                 bufferStride_all_positive = false;
    1931           1 :                 break;
    1932             :             }
    1933             :         }
    1934             :     }
    1935       13712 :     for (size_t i = 0; i < dims.size(); i++)
    1936             :     {
    1937        8866 :         if (arrayStartIdx[i] >= dims[i]->GetSize())
    1938             :         {
    1939           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    1940             :                      "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
    1941             :                      static_cast<unsigned>(i),
    1942           2 :                      static_cast<GUInt64>(arrayStartIdx[i]),
    1943           2 :                      static_cast<GUInt64>(dims[i]->GetSize()));
    1944           2 :             return false;
    1945             :         }
    1946             :         bool bOverflow;
    1947        8864 :         if (arrayStep[i] >= 0)
    1948             :         {
    1949             :             try
    1950             :             {
    1951        8270 :                 bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
    1952        8272 :                              CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    1953       33083 :                                  CPLSM(static_cast<uint64_t>(arrayStep[i])))
    1954        8270 :                                 .v() >= dims[i]->GetSize();
    1955             :             }
    1956           1 :             catch (...)
    1957             :             {
    1958           1 :                 bOverflow = true;
    1959             :             }
    1960        8271 :             if (bOverflow)
    1961             :             {
    1962           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1963             :                          "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
    1964             :                          ">= " CPL_FRMT_GUIB,
    1965             :                          static_cast<unsigned>(i), static_cast<unsigned>(i),
    1966             :                          static_cast<unsigned>(i),
    1967           5 :                          static_cast<GUInt64>(dims[i]->GetSize()));
    1968           5 :                 return false;
    1969             :             }
    1970             :         }
    1971             :         else
    1972             :         {
    1973             :             try
    1974             :             {
    1975         593 :                 bOverflow =
    1976         593 :                     arrayStartIdx[i] <
    1977         593 :                     (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    1978        1186 :                      CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
    1979             :                                ? (static_cast<uint64_t>(1) << 63)
    1980        1186 :                                : static_cast<uint64_t>(-arrayStep[i])))
    1981         593 :                         .v();
    1982             :             }
    1983           0 :             catch (...)
    1984             :             {
    1985           0 :                 bOverflow = true;
    1986             :             }
    1987         593 :             if (bOverflow)
    1988             :             {
    1989           3 :                 CPLError(
    1990             :                     CE_Failure, CPLE_AppDefined,
    1991             :                     "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
    1992             :                     static_cast<unsigned>(i), static_cast<unsigned>(i),
    1993             :                     static_cast<unsigned>(i));
    1994           3 :                 return false;
    1995             :             }
    1996             :         }
    1997             :     }
    1998             : 
    1999        4846 :     if (buffer_alloc_start)
    2000             :     {
    2001        2539 :         const size_t elementSize = bufferDataType.GetSize();
    2002        2539 :         const GByte *paby_buffer = static_cast<const GByte *>(buffer);
    2003        2539 :         const GByte *paby_buffer_alloc_start =
    2004             :             static_cast<const GByte *>(buffer_alloc_start);
    2005        2539 :         const GByte *paby_buffer_alloc_end =
    2006             :             paby_buffer_alloc_start + buffer_alloc_size;
    2007        2539 :         if (bufferStride_all_positive)
    2008             :         {
    2009        2539 :             if (paby_buffer < paby_buffer_alloc_start)
    2010             :             {
    2011           0 :                 lamda_error();
    2012           0 :                 return false;
    2013             :             }
    2014        2539 :             GUInt64 nOffset = elementSize;
    2015        7325 :             for (size_t i = 0; i < dims.size(); i++)
    2016             :             {
    2017             :                 try
    2018             :                 {
    2019        4786 :                     nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
    2020        4786 :                                CPLSM(static_cast<uint64_t>(bufferStride[i])) *
    2021        9572 :                                    CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    2022       19144 :                                    CPLSM(static_cast<uint64_t>(elementSize)))
    2023        4786 :                                   .v();
    2024             :                 }
    2025           0 :                 catch (...)
    2026             :                 {
    2027           0 :                     lamda_error();
    2028           0 :                     return false;
    2029             :                 }
    2030             :             }
    2031             : #if SIZEOF_VOIDP == 4
    2032             :             if (static_cast<size_t>(nOffset) != nOffset)
    2033             :             {
    2034             :                 lamda_error();
    2035             :                 return false;
    2036             :             }
    2037             : #endif
    2038        2539 :             if (paby_buffer + nOffset > paby_buffer_alloc_end)
    2039             :             {
    2040           0 :                 lamda_error();
    2041           0 :                 return false;
    2042             :             }
    2043             :         }
    2044           0 :         else if (dims.size() < 31)
    2045             :         {
    2046             :             // Check all corners of the hypercube
    2047           0 :             const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
    2048           0 :             for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
    2049             :             {
    2050           0 :                 const GByte *paby = paby_buffer;
    2051           0 :                 for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
    2052             :                      i++)
    2053             :                 {
    2054           0 :                     if (iCornerCode & (1U << i))
    2055             :                     {
    2056             :                         // We should check for integer overflows
    2057           0 :                         paby += bufferStride[i] * (count[i] - 1) * elementSize;
    2058             :                     }
    2059             :                 }
    2060           0 :                 if (paby < paby_buffer_alloc_start ||
    2061           0 :                     paby + elementSize > paby_buffer_alloc_end)
    2062             :                 {
    2063           0 :                     lamda_error();
    2064           0 :                     return false;
    2065             :                 }
    2066             :             }
    2067             :         }
    2068             :     }
    2069             : 
    2070        4846 :     return true;
    2071             : }
    2072             : 
    2073             : //! @endcond
    2074             : 
    2075             : /************************************************************************/
    2076             : /*                               Read()                                 */
    2077             : /************************************************************************/
    2078             : 
    2079             : /** Read part or totality of a multidimensional array or attribute.
    2080             :  *
    2081             :  * This will extract the content of a hyper-rectangle from the array into
    2082             :  * a user supplied buffer.
    2083             :  *
    2084             :  * If bufferDataType is of type string, the values written in pDstBuffer
    2085             :  * will be char* pointers and the strings should be freed with CPLFree().
    2086             :  *
    2087             :  * This is the same as the C function GDALMDArrayRead().
    2088             :  *
    2089             :  * @param arrayStartIdx Values representing the starting index to read
    2090             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2091             :  *                      Array of GetDimensionCount() values. Must not be
    2092             :  *                      nullptr, unless for a zero-dimensional array.
    2093             :  *
    2094             :  * @param count         Values representing the number of values to extract in
    2095             :  *                      each dimension.
    2096             :  *                      Array of GetDimensionCount() values. Must not be
    2097             :  *                      nullptr, unless for a zero-dimensional array.
    2098             :  *
    2099             :  * @param arrayStep     Spacing between values to extract in each dimension.
    2100             :  *                      The spacing is in number of array elements, not bytes.
    2101             :  *                      If provided, must contain GetDimensionCount() values.
    2102             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
    2103             :  * default to indicate consecutive elements.
    2104             :  *
    2105             :  * @param bufferStride  Spacing between values to store in pDstBuffer.
    2106             :  *                      The spacing is in number of array elements, not bytes.
    2107             :  *                      If provided, must contain GetDimensionCount() values.
    2108             :  *                      Negative values are possible (for example to reorder
    2109             :  *                      from bottom-to-top to top-to-bottom).
    2110             :  *                      If set to nullptr, will be set so that pDstBuffer is
    2111             :  *                      written in a compact way, with elements of the last /
    2112             :  *                      fastest varying dimension being consecutive.
    2113             :  *
    2114             :  * @param bufferDataType Data type of values in pDstBuffer.
    2115             :  *
    2116             :  * @param pDstBuffer    User buffer to store the values read. Should be big
    2117             :  *                      enough to store the number of values indicated by
    2118             :  * count[] and with the spacing of bufferStride[].
    2119             :  *
    2120             :  * @param pDstBufferAllocStart Optional pointer that can be used to validate the
    2121             :  *                             validity of pDstBuffer. pDstBufferAllocStart
    2122             :  * should be the pointer returned by the malloc() or equivalent call used to
    2123             :  * allocate the buffer. It will generally be equal to pDstBuffer (when
    2124             :  * bufferStride[] values are all positive), but not necessarily. If specified,
    2125             :  * nDstBufferAllocSize should be also set to the appropriate value. If no
    2126             :  * validation is needed, nullptr can be passed.
    2127             :  *
    2128             :  * @param nDstBufferAllocSize  Optional buffer size, that can be used to
    2129             :  * validate the validity of pDstBuffer. This is the size of the buffer starting
    2130             :  * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
    2131             :  *                             set to the appropriate value.
    2132             :  *                             If no validation is needed, 0 can be passed.
    2133             :  *
    2134             :  * @return true in case of success.
    2135             :  */
    2136        2345 : bool GDALAbstractMDArray::Read(
    2137             :     const GUInt64 *arrayStartIdx, const size_t *count,
    2138             :     const GInt64 *arrayStep,         // step in elements
    2139             :     const GPtrDiff_t *bufferStride,  // stride in elements
    2140             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    2141             :     const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
    2142             : {
    2143        2345 :     if (!GetDataType().CanConvertTo(bufferDataType))
    2144             :     {
    2145           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2146             :                  "Array data type is not convertible to buffer data type");
    2147           0 :         return false;
    2148             :     }
    2149             : 
    2150        4690 :     std::vector<GInt64> tmp_arrayStep;
    2151        4690 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    2152        2345 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    2153             :                               bufferDataType, pDstBuffer, pDstBufferAllocStart,
    2154             :                               nDstBufferAllocSize, tmp_arrayStep,
    2155             :                               tmp_bufferStride))
    2156             :     {
    2157           0 :         return false;
    2158             :     }
    2159             : 
    2160        2345 :     return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
    2161        2345 :                  pDstBuffer);
    2162             : }
    2163             : 
    2164             : /************************************************************************/
    2165             : /*                                IWrite()                              */
    2166             : /************************************************************************/
    2167             : 
    2168             : //! @cond Doxygen_Suppress
    2169           1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
    2170             :                                  const GInt64 *, const GPtrDiff_t *,
    2171             :                                  const GDALExtendedDataType &, const void *)
    2172             : {
    2173           1 :     CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
    2174           1 :     return false;
    2175             : }
    2176             : 
    2177             : //! @endcond
    2178             : 
    2179             : /************************************************************************/
    2180             : /*                               Write()                                 */
    2181             : /************************************************************************/
    2182             : 
    2183             : /** Write part or totality of a multidimensional array or attribute.
    2184             :  *
    2185             :  * This will set the content of a hyper-rectangle into the array from
    2186             :  * a user supplied buffer.
    2187             :  *
    2188             :  * If bufferDataType is of type string, the values read from pSrcBuffer
    2189             :  * will be char* pointers.
    2190             :  *
    2191             :  * This is the same as the C function GDALMDArrayWrite().
    2192             :  *
    2193             :  * @param arrayStartIdx Values representing the starting index to write
    2194             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2195             :  *                      Array of GetDimensionCount() values. Must not be
    2196             :  *                      nullptr, unless for a zero-dimensional array.
    2197             :  *
    2198             :  * @param count         Values representing the number of values to write in
    2199             :  *                      each dimension.
    2200             :  *                      Array of GetDimensionCount() values. Must not be
    2201             :  *                      nullptr, unless for a zero-dimensional array.
    2202             :  *
    2203             :  * @param arrayStep     Spacing between values to write in each dimension.
    2204             :  *                      The spacing is in number of array elements, not bytes.
    2205             :  *                      If provided, must contain GetDimensionCount() values.
    2206             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
    2207             :  * default to indicate consecutive elements.
    2208             :  *
    2209             :  * @param bufferStride  Spacing between values to read from pSrcBuffer.
    2210             :  *                      The spacing is in number of array elements, not bytes.
    2211             :  *                      If provided, must contain GetDimensionCount() values.
    2212             :  *                      Negative values are possible (for example to reorder
    2213             :  *                      from bottom-to-top to top-to-bottom).
    2214             :  *                      If set to nullptr, will be set so that pSrcBuffer is
    2215             :  *                      written in a compact way, with elements of the last /
    2216             :  *                      fastest varying dimension being consecutive.
    2217             :  *
    2218             :  * @param bufferDataType Data type of values in pSrcBuffer.
    2219             :  *
    2220             :  * @param pSrcBuffer    User buffer to read the values from. Should be big
    2221             :  *                      enough to store the number of values indicated by
    2222             :  * count[] and with the spacing of bufferStride[].
    2223             :  *
    2224             :  * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
    2225             :  *                             validity of pSrcBuffer. pSrcBufferAllocStart
    2226             :  * should be the pointer returned by the malloc() or equivalent call used to
    2227             :  * allocate the buffer. It will generally be equal to pSrcBuffer (when
    2228             :  * bufferStride[] values are all positive), but not necessarily. If specified,
    2229             :  * nSrcBufferAllocSize should be also set to the appropriate value. If no
    2230             :  * validation is needed, nullptr can be passed.
    2231             :  *
    2232             :  * @param nSrcBufferAllocSize  Optional buffer size, that can be used to
    2233             :  * validate the validity of pSrcBuffer. This is the size of the buffer starting
    2234             :  * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
    2235             :  *                             set to the appropriate value.
    2236             :  *                             If no validation is needed, 0 can be passed.
    2237             :  *
    2238             :  * @return true in case of success.
    2239             :  */
    2240        1788 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
    2241             :                                 const size_t *count, const GInt64 *arrayStep,
    2242             :                                 const GPtrDiff_t *bufferStride,
    2243             :                                 const GDALExtendedDataType &bufferDataType,
    2244             :                                 const void *pSrcBuffer,
    2245             :                                 const void *pSrcBufferAllocStart,
    2246             :                                 size_t nSrcBufferAllocSize)
    2247             : {
    2248        1788 :     if (!bufferDataType.CanConvertTo(GetDataType()))
    2249             :     {
    2250           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2251             :                  "Buffer data type is not convertible to array data type");
    2252           0 :         return false;
    2253             :     }
    2254             : 
    2255        3576 :     std::vector<GInt64> tmp_arrayStep;
    2256        3576 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    2257        1788 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    2258             :                               bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
    2259             :                               nSrcBufferAllocSize, tmp_arrayStep,
    2260             :                               tmp_bufferStride))
    2261             :     {
    2262           0 :         return false;
    2263             :     }
    2264             : 
    2265        1788 :     return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
    2266        1788 :                   pSrcBuffer);
    2267             : }
    2268             : 
    2269             : /************************************************************************/
    2270             : /*                          GetTotalElementsCount()                     */
    2271             : /************************************************************************/
    2272             : 
    2273             : /** Return the total number of values in the array.
    2274             :  *
    2275             :  * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
    2276             :  * and GDALAttributeGetTotalElementsCount().
    2277             :  *
    2278             :  */
    2279        1022 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
    2280             : {
    2281        1022 :     const auto &dims = GetDimensions();
    2282        1022 :     if (dims.empty())
    2283         504 :         return 1;
    2284         518 :     GUInt64 nElts = 1;
    2285        1146 :     for (const auto &dim : dims)
    2286             :     {
    2287             :         try
    2288             :         {
    2289         628 :             nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
    2290        1884 :                      CPLSM(static_cast<uint64_t>(dim->GetSize())))
    2291         628 :                         .v();
    2292             :         }
    2293           0 :         catch (...)
    2294             :         {
    2295           0 :             return 0;
    2296             :         }
    2297             :     }
    2298         518 :     return nElts;
    2299             : }
    2300             : 
    2301             : /************************************************************************/
    2302             : /*                           GetBlockSize()                             */
    2303             : /************************************************************************/
    2304             : 
    2305             : /** Return the "natural" block size of the array along all dimensions.
    2306             :  *
    2307             :  * Some drivers might organize the array in tiles/blocks and reading/writing
    2308             :  * aligned on those tile/block boundaries will be more efficient.
    2309             :  *
    2310             :  * The returned number of elements in the vector is the same as
    2311             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
    2312             :  * the natural block size along the considered dimension.
    2313             :  * "Flat" arrays will typically return a vector of values set to 0.
    2314             :  *
    2315             :  * The default implementation will return a vector of values set to 0.
    2316             :  *
    2317             :  * This method is used by GetProcessingChunkSize().
    2318             :  *
    2319             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
    2320             :  * theoretical case of a 32-bit platform, this might exceed its size_t
    2321             :  * allocation capabilities.
    2322             :  *
    2323             :  * This is the same as the C function GDALMDArrayGetBlockSize().
    2324             :  *
    2325             :  * @return the block size, in number of elements along each dimension.
    2326             :  */
    2327         221 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
    2328             : {
    2329         221 :     return std::vector<GUInt64>(GetDimensionCount());
    2330             : }
    2331             : 
    2332             : /************************************************************************/
    2333             : /*                       GetProcessingChunkSize()                       */
    2334             : /************************************************************************/
    2335             : 
    2336             : /** \brief Return an optimal chunk size for read/write operations, given the
    2337             :  * natural block size and memory constraints specified.
    2338             :  *
    2339             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
    2340             :  * multiple of those returned by GetBlockSize() (unless the block define by
    2341             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
    2342             :  * returned by this method).
    2343             :  *
    2344             :  * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
    2345             :  *
    2346             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
    2347             :  * chunk.
    2348             :  *
    2349             :  * @return the chunk size, in number of elements along each dimension.
    2350             :  */
    2351             : std::vector<size_t>
    2352          60 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
    2353             : {
    2354          60 :     const auto &dims = GetDimensions();
    2355          60 :     const auto &nDTSize = GetDataType().GetSize();
    2356          60 :     std::vector<size_t> anChunkSize;
    2357         120 :     auto blockSize = GetBlockSize();
    2358          60 :     CPLAssert(blockSize.size() == dims.size());
    2359          60 :     size_t nChunkSize = nDTSize;
    2360          60 :     bool bOverflow = false;
    2361          60 :     constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
    2362             :     // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
    2363             :     // [1, min(sizet_max, dim_size[i])]
    2364             :     // Also make sure that the product of all anChunkSize[i]) fits on size_t
    2365         173 :     for (size_t i = 0; i < dims.size(); i++)
    2366             :     {
    2367             :         const auto sizeDimI =
    2368         226 :             std::max(static_cast<size_t>(1),
    2369         226 :                      static_cast<size_t>(
    2370         226 :                          std::min(static_cast<GUInt64>(kSIZE_T_MAX),
    2371         113 :                                   std::min(blockSize[i], dims[i]->GetSize()))));
    2372         113 :         anChunkSize.push_back(sizeDimI);
    2373         113 :         if (nChunkSize > kSIZE_T_MAX / sizeDimI)
    2374             :         {
    2375           4 :             bOverflow = true;
    2376             :         }
    2377             :         else
    2378             :         {
    2379         109 :             nChunkSize *= sizeDimI;
    2380             :         }
    2381             :     }
    2382          60 :     if (nChunkSize == 0)
    2383           0 :         return anChunkSize;
    2384             : 
    2385             :     // If the product of all anChunkSize[i] does not fit on size_t, then
    2386             :     // set lowest anChunkSize[i] to 1.
    2387          60 :     if (bOverflow)
    2388             :     {
    2389           2 :         nChunkSize = nDTSize;
    2390           2 :         bOverflow = false;
    2391           8 :         for (size_t i = dims.size(); i > 0;)
    2392             :         {
    2393           6 :             --i;
    2394           6 :             if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
    2395             :             {
    2396           4 :                 bOverflow = true;
    2397           4 :                 anChunkSize[i] = 1;
    2398             :             }
    2399             :             else
    2400             :             {
    2401           2 :                 nChunkSize *= anChunkSize[i];
    2402             :             }
    2403             :         }
    2404             :     }
    2405             : 
    2406          60 :     nChunkSize = nDTSize;
    2407         120 :     std::vector<size_t> anAccBlockSizeFromStart;
    2408         173 :     for (size_t i = 0; i < dims.size(); i++)
    2409             :     {
    2410         113 :         nChunkSize *= anChunkSize[i];
    2411         113 :         anAccBlockSizeFromStart.push_back(nChunkSize);
    2412             :     }
    2413          60 :     if (nChunkSize <= nMaxChunkMemory / 2)
    2414             :     {
    2415          56 :         size_t nVoxelsFromEnd = 1;
    2416         161 :         for (size_t i = dims.size(); i > 0;)
    2417             :         {
    2418         105 :             --i;
    2419             :             const auto nCurBlockSize =
    2420         105 :                 anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
    2421         105 :             const auto nMul = nMaxChunkMemory / nCurBlockSize;
    2422         105 :             if (nMul >= 2)
    2423             :             {
    2424          97 :                 const auto nSizeThisDim(dims[i]->GetSize());
    2425             :                 const auto nBlocksThisDim =
    2426          97 :                     DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
    2427          97 :                 anChunkSize[i] = static_cast<size_t>(std::min(
    2428          97 :                     anChunkSize[i] *
    2429         194 :                         std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
    2430          97 :                     nSizeThisDim));
    2431             :             }
    2432         105 :             nVoxelsFromEnd *= anChunkSize[i];
    2433             :         }
    2434             :     }
    2435          60 :     return anChunkSize;
    2436             : }
    2437             : 
    2438             : /************************************************************************/
    2439             : /*                         BaseRename()                                 */
    2440             : /************************************************************************/
    2441             : 
    2442             : //! @cond Doxygen_Suppress
    2443          18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
    2444             : {
    2445          18 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
    2446          18 :     m_osFullName += osNewName;
    2447          18 :     m_osName = osNewName;
    2448             : 
    2449          18 :     NotifyChildrenOfRenaming();
    2450          18 : }
    2451             : 
    2452             : //! @endcond
    2453             : 
    2454             : //! @cond Doxygen_Suppress
    2455             : /************************************************************************/
    2456             : /*                          ParentRenamed()                             */
    2457             : /************************************************************************/
    2458             : 
    2459          50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
    2460             : {
    2461          50 :     m_osFullName = osNewParentFullName;
    2462          50 :     m_osFullName += "/";
    2463          50 :     m_osFullName += m_osName;
    2464             : 
    2465          50 :     NotifyChildrenOfRenaming();
    2466          50 : }
    2467             : 
    2468             : //! @endcond
    2469             : 
    2470             : /************************************************************************/
    2471             : /*                             Deleted()                                */
    2472             : /************************************************************************/
    2473             : 
    2474             : //! @cond Doxygen_Suppress
    2475          52 : void GDALAbstractMDArray::Deleted()
    2476             : {
    2477          52 :     m_bValid = false;
    2478             : 
    2479          52 :     NotifyChildrenOfDeletion();
    2480          52 : }
    2481             : 
    2482             : //! @endcond
    2483             : 
    2484             : /************************************************************************/
    2485             : /*                        ParentDeleted()                               */
    2486             : /************************************************************************/
    2487             : 
    2488             : //! @cond Doxygen_Suppress
    2489          28 : void GDALAbstractMDArray::ParentDeleted()
    2490             : {
    2491          28 :     Deleted();
    2492          28 : }
    2493             : 
    2494             : //! @endcond
    2495             : 
    2496             : /************************************************************************/
    2497             : /*                     CheckValidAndErrorOutIfNot()                     */
    2498             : /************************************************************************/
    2499             : 
    2500             : //! @cond Doxygen_Suppress
    2501        5702 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
    2502             : {
    2503        5702 :     if (!m_bValid)
    2504             :     {
    2505          26 :         CPLError(CE_Failure, CPLE_AppDefined,
    2506             :                  "This object has been deleted. No action on it is possible");
    2507             :     }
    2508        5702 :     return m_bValid;
    2509             : }
    2510             : 
    2511             : //! @endcond
    2512             : 
    2513             : /************************************************************************/
    2514             : /*                             SetUnit()                                */
    2515             : /************************************************************************/
    2516             : 
    2517             : /** Set the variable unit.
    2518             :  *
    2519             :  * Values should conform as much as possible with those allowed by
    2520             :  * the NetCDF CF conventions:
    2521             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
    2522             :  * but others might be returned.
    2523             :  *
    2524             :  * Few examples are "meter", "degrees", "second", ...
    2525             :  * Empty value means unknown.
    2526             :  *
    2527             :  * This is the same as the C function GDALMDArraySetUnit()
    2528             :  *
    2529             :  * @note Driver implementation: optionally implemented.
    2530             :  *
    2531             :  * @param osUnit unit name.
    2532             :  * @return true in case of success.
    2533             :  */
    2534           0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
    2535             : {
    2536           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
    2537           0 :     return false;
    2538             : }
    2539             : 
    2540             : /************************************************************************/
    2541             : /*                             GetUnit()                                */
    2542             : /************************************************************************/
    2543             : 
    2544             : /** Return the array unit.
    2545             :  *
    2546             :  * Values should conform as much as possible with those allowed by
    2547             :  * the NetCDF CF conventions:
    2548             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
    2549             :  * but others might be returned.
    2550             :  *
    2551             :  * Few examples are "meter", "degrees", "second", ...
    2552             :  * Empty value means unknown.
    2553             :  *
    2554             :  * This is the same as the C function GDALMDArrayGetUnit()
    2555             :  */
    2556           5 : const std::string &GDALMDArray::GetUnit() const
    2557             : {
    2558           5 :     static const std::string emptyString;
    2559           5 :     return emptyString;
    2560             : }
    2561             : 
    2562             : /************************************************************************/
    2563             : /*                          SetSpatialRef()                             */
    2564             : /************************************************************************/
    2565             : 
    2566             : /** Assign a spatial reference system object to the array.
    2567             :  *
    2568             :  * This is the same as the C function GDALMDArraySetSpatialRef().
    2569             :  */
    2570           0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
    2571             : {
    2572           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
    2573           0 :     return false;
    2574             : }
    2575             : 
    2576             : /************************************************************************/
    2577             : /*                          GetSpatialRef()                             */
    2578             : /************************************************************************/
    2579             : 
    2580             : /** Return the spatial reference system object associated with the array.
    2581             :  *
    2582             :  * This is the same as the C function GDALMDArrayGetSpatialRef().
    2583             :  */
    2584           4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
    2585             : {
    2586           4 :     return nullptr;
    2587             : }
    2588             : 
    2589             : /************************************************************************/
    2590             : /*                        GetRawNoDataValue()                           */
    2591             : /************************************************************************/
    2592             : 
    2593             : /** Return the nodata value as a "raw" value.
    2594             :  *
    2595             :  * The value returned might be nullptr in case of no nodata value. When
    2596             :  * a nodata value is registered, a non-nullptr will be returned whose size in
    2597             :  * bytes is GetDataType().GetSize().
    2598             :  *
    2599             :  * The returned value should not be modified or freed. It is valid until
    2600             :  * the array is destroyed, or the next call to GetRawNoDataValue() or
    2601             :  * SetRawNoDataValue(), or any similar methods.
    2602             :  *
    2603             :  * @note Driver implementation: this method shall be implemented if nodata
    2604             :  * is supported.
    2605             :  *
    2606             :  * This is the same as the C function GDALMDArrayGetRawNoDataValue().
    2607             :  *
    2608             :  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
    2609             :  */
    2610           5 : const void *GDALMDArray::GetRawNoDataValue() const
    2611             : {
    2612           5 :     return nullptr;
    2613             : }
    2614             : 
    2615             : /************************************************************************/
    2616             : /*                        GetNoDataValueAsDouble()                      */
    2617             : /************************************************************************/
    2618             : 
    2619             : /** Return the nodata value as a double.
    2620             :  *
    2621             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
    2622             :  *
    2623             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2624             :  * a nodata value exists and can be converted to double. Might be nullptr.
    2625             :  *
    2626             :  * @return the nodata value as a double. A 0.0 value might also indicate the
    2627             :  * absence of a nodata value or an error in the conversion (*pbHasNoData will be
    2628             :  * set to false then).
    2629             :  */
    2630       22420 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
    2631             : {
    2632       22420 :     const void *pNoData = GetRawNoDataValue();
    2633       22420 :     double dfNoData = 0.0;
    2634       22420 :     const auto &eDT = GetDataType();
    2635       22420 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2636       22420 :     if (ok)
    2637             :     {
    2638       22183 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
    2639             :                         GDT_Float64, 0, 1);
    2640             :     }
    2641       22420 :     if (pbHasNoData)
    2642         385 :         *pbHasNoData = ok;
    2643       22420 :     return dfNoData;
    2644             : }
    2645             : 
    2646             : /************************************************************************/
    2647             : /*                        GetNoDataValueAsInt64()                       */
    2648             : /************************************************************************/
    2649             : 
    2650             : /** Return the nodata value as a Int64.
    2651             :  *
    2652             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2653             :  * a nodata value exists and can be converted to Int64. Might be nullptr.
    2654             :  *
    2655             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
    2656             :  *
    2657             :  * @return the nodata value as a Int64
    2658             :  *
    2659             :  * @since GDAL 3.5
    2660             :  */
    2661          12 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
    2662             : {
    2663          12 :     const void *pNoData = GetRawNoDataValue();
    2664          12 :     int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
    2665          12 :     const auto &eDT = GetDataType();
    2666          12 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2667          12 :     if (ok)
    2668             :     {
    2669           8 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
    2670             :                         GDT_Int64, 0, 1);
    2671             :     }
    2672          12 :     if (pbHasNoData)
    2673          12 :         *pbHasNoData = ok;
    2674          12 :     return nNoData;
    2675             : }
    2676             : 
    2677             : /************************************************************************/
    2678             : /*                       GetNoDataValueAsUInt64()                       */
    2679             : /************************************************************************/
    2680             : 
    2681             : /** Return the nodata value as a UInt64.
    2682             :  *
    2683             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
    2684             : 
    2685             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2686             :  * a nodata value exists and can be converted to UInt64. Might be nullptr.
    2687             :  *
    2688             :  * @return the nodata value as a UInt64
    2689             :  *
    2690             :  * @since GDAL 3.5
    2691             :  */
    2692           8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
    2693             : {
    2694           8 :     const void *pNoData = GetRawNoDataValue();
    2695           8 :     uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
    2696           8 :     const auto &eDT = GetDataType();
    2697           8 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2698           8 :     if (ok)
    2699             :     {
    2700           6 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
    2701             :                         GDT_UInt64, 0, 1);
    2702             :     }
    2703           8 :     if (pbHasNoData)
    2704           8 :         *pbHasNoData = ok;
    2705           8 :     return nNoData;
    2706             : }
    2707             : 
    2708             : /************************************************************************/
    2709             : /*                        SetRawNoDataValue()                           */
    2710             : /************************************************************************/
    2711             : 
    2712             : /** Set the nodata value as a "raw" value.
    2713             :  *
    2714             :  * The value passed might be nullptr in case of no nodata value. When
    2715             :  * a nodata value is registered, a non-nullptr whose size in
    2716             :  * bytes is GetDataType().GetSize() must be passed.
    2717             :  *
    2718             :  * This is the same as the C function GDALMDArraySetRawNoDataValue().
    2719             :  *
    2720             :  * @note Driver implementation: this method shall be implemented if setting
    2721             :  nodata
    2722             :  * is supported.
    2723             : 
    2724             :  * @return true in case of success.
    2725             :  */
    2726           0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
    2727             : {
    2728           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2729             :              "SetRawNoDataValue() not implemented");
    2730           0 :     return false;
    2731             : }
    2732             : 
    2733             : /************************************************************************/
    2734             : /*                           SetNoDataValue()                           */
    2735             : /************************************************************************/
    2736             : 
    2737             : /** Set the nodata value as a double.
    2738             :  *
    2739             :  * If the natural data type of the attribute/array is not double, type
    2740             :  * conversion will occur to the type returned by GetDataType().
    2741             :  *
    2742             :  * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
    2743             :  *
    2744             :  * @return true in case of success.
    2745             :  */
    2746          57 : bool GDALMDArray::SetNoDataValue(double dfNoData)
    2747             : {
    2748          57 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2749          57 :     bool bRet = false;
    2750          57 :     if (GDALExtendedDataType::CopyValue(
    2751         114 :             &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
    2752          57 :             GetDataType()))
    2753             :     {
    2754          57 :         bRet = SetRawNoDataValue(pRawNoData);
    2755             :     }
    2756          57 :     CPLFree(pRawNoData);
    2757          57 :     return bRet;
    2758             : }
    2759             : 
    2760             : /************************************************************************/
    2761             : /*                           SetNoDataValue()                           */
    2762             : /************************************************************************/
    2763             : 
    2764             : /** Set the nodata value as a Int64.
    2765             :  *
    2766             :  * If the natural data type of the attribute/array is not Int64, type conversion
    2767             :  * will occur to the type returned by GetDataType().
    2768             :  *
    2769             :  * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
    2770             :  *
    2771             :  * @return true in case of success.
    2772             :  *
    2773             :  * @since GDAL 3.5
    2774             :  */
    2775           3 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
    2776             : {
    2777           3 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2778           3 :     bool bRet = false;
    2779           3 :     if (GDALExtendedDataType::CopyValue(&nNoData,
    2780           6 :                                         GDALExtendedDataType::Create(GDT_Int64),
    2781           3 :                                         pRawNoData, GetDataType()))
    2782             :     {
    2783           3 :         bRet = SetRawNoDataValue(pRawNoData);
    2784             :     }
    2785           3 :     CPLFree(pRawNoData);
    2786           3 :     return bRet;
    2787             : }
    2788             : 
    2789             : /************************************************************************/
    2790             : /*                           SetNoDataValue()                           */
    2791             : /************************************************************************/
    2792             : 
    2793             : /** Set the nodata value as a Int64.
    2794             :  *
    2795             :  * If the natural data type of the attribute/array is not Int64, type conversion
    2796             :  * will occur to the type returned by GetDataType().
    2797             :  *
    2798             :  * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
    2799             :  *
    2800             :  * @return true in case of success.
    2801             :  *
    2802             :  * @since GDAL 3.5
    2803             :  */
    2804           1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
    2805             : {
    2806           1 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2807           1 :     bool bRet = false;
    2808           1 :     if (GDALExtendedDataType::CopyValue(
    2809           2 :             &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
    2810           1 :             GetDataType()))
    2811             :     {
    2812           1 :         bRet = SetRawNoDataValue(pRawNoData);
    2813             :     }
    2814           1 :     CPLFree(pRawNoData);
    2815           1 :     return bRet;
    2816             : }
    2817             : 
    2818             : /************************************************************************/
    2819             : /*                            Resize()                                  */
    2820             : /************************************************************************/
    2821             : 
    2822             : /** Resize an array to new dimensions.
    2823             :  *
    2824             :  * Not all drivers may allow this operation, and with restrictions (e.g.
    2825             :  * for netCDF, this is limited to growing of "unlimited" dimensions)
    2826             :  *
    2827             :  * Resizing a dimension used in other arrays will cause those other arrays
    2828             :  * to be resized.
    2829             :  *
    2830             :  * This is the same as the C function GDALMDArrayResize().
    2831             :  *
    2832             :  * @param anNewDimSizes Array of GetDimensionCount() values containing the
    2833             :  *                      new size of each indexing dimension.
    2834             :  * @param papszOptions Options. (Driver specific)
    2835             :  * @return true in case of success.
    2836             :  * @since GDAL 3.7
    2837             :  */
    2838           0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
    2839             :                          CPL_UNUSED CSLConstList papszOptions)
    2840             : {
    2841           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2842             :              "Resize() is not supported for this array");
    2843           0 :     return false;
    2844             : }
    2845             : 
    2846             : /************************************************************************/
    2847             : /*                               SetScale()                             */
    2848             : /************************************************************************/
    2849             : 
    2850             : /** Set the scale value to apply to raw values.
    2851             :  *
    2852             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2853             :  *
    2854             :  * This is the same as the C function GDALMDArraySetScale() /
    2855             :  * GDALMDArraySetScaleEx().
    2856             :  *
    2857             :  * @note Driver implementation: this method shall be implemented if setting
    2858             :  * scale is supported.
    2859             :  *
    2860             :  * @param dfScale scale
    2861             :  * @param eStorageType Data type to which create the potential attribute that
    2862             :  * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
    2863             :  * implementation will decide automatically the data type. Note that changing
    2864             :  * the data type after initial setting might not be supported.
    2865             :  * @return true in case of success.
    2866             :  */
    2867           0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
    2868             :                            CPL_UNUSED GDALDataType eStorageType)
    2869             : {
    2870           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
    2871           0 :     return false;
    2872             : }
    2873             : 
    2874             : /************************************************************************/
    2875             : /*                               SetOffset)                             */
    2876             : /************************************************************************/
    2877             : 
    2878             : /** Set the offset value to apply to raw values.
    2879             :  *
    2880             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2881             :  *
    2882             :  * This is the same as the C function GDALMDArraySetOffset() /
    2883             :  * GDALMDArraySetOffsetEx().
    2884             :  *
    2885             :  * @note Driver implementation: this method shall be implemented if setting
    2886             :  * offset is supported.
    2887             :  *
    2888             :  * @param dfOffset Offset
    2889             :  * @param eStorageType Data type to which create the potential attribute that
    2890             :  * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
    2891             :  * implementation will decide automatically the data type. Note that changing
    2892             :  * the data type after initial setting might not be supported.
    2893             :  * @return true in case of success.
    2894             :  */
    2895           0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
    2896             :                             CPL_UNUSED GDALDataType eStorageType)
    2897             : {
    2898           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
    2899           0 :     return false;
    2900             : }
    2901             : 
    2902             : /************************************************************************/
    2903             : /*                               GetScale()                             */
    2904             : /************************************************************************/
    2905             : 
    2906             : /** Get the scale value to apply to raw values.
    2907             :  *
    2908             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2909             :  *
    2910             :  * This is the same as the C function GDALMDArrayGetScale().
    2911             :  *
    2912             :  * @note Driver implementation: this method shall be implemented if gettings
    2913             :  * scale is supported.
    2914             :  *
    2915             :  * @param pbHasScale Pointer to a output boolean that will be set to true if
    2916             :  * a scale value exists. Might be nullptr.
    2917             :  * @param peStorageType Pointer to a output GDALDataType that will be set to
    2918             :  * the storage type of the scale value, when known/relevant. Otherwise will be
    2919             :  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
    2920             :  *
    2921             :  * @return the scale value. A 1.0 value might also indicate the
    2922             :  * absence of a scale value.
    2923             :  */
    2924          13 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
    2925             :                              CPL_UNUSED GDALDataType *peStorageType) const
    2926             : {
    2927          13 :     if (pbHasScale)
    2928          13 :         *pbHasScale = false;
    2929          13 :     return 1.0;
    2930             : }
    2931             : 
    2932             : /************************************************************************/
    2933             : /*                               GetOffset()                            */
    2934             : /************************************************************************/
    2935             : 
    2936             : /** Get the offset value to apply to raw values.
    2937             :  *
    2938             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2939             :  *
    2940             :  * This is the same as the C function GDALMDArrayGetOffset().
    2941             :  *
    2942             :  * @note Driver implementation: this method shall be implemented if gettings
    2943             :  * offset is supported.
    2944             :  *
    2945             :  * @param pbHasOffset Pointer to a output boolean that will be set to true if
    2946             :  * a offset value exists. Might be nullptr.
    2947             :  * @param peStorageType Pointer to a output GDALDataType that will be set to
    2948             :  * the storage type of the offset value, when known/relevant. Otherwise will be
    2949             :  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
    2950             :  *
    2951             :  * @return the offset value. A 0.0 value might also indicate the
    2952             :  * absence of a offset value.
    2953             :  */
    2954          13 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
    2955             :                               CPL_UNUSED GDALDataType *peStorageType) const
    2956             : {
    2957          13 :     if (pbHasOffset)
    2958          13 :         *pbHasOffset = false;
    2959          13 :     return 0.0;
    2960             : }
    2961             : 
    2962             : /************************************************************************/
    2963             : /*                         ProcessPerChunk()                            */
    2964             : /************************************************************************/
    2965             : 
    2966             : namespace
    2967             : {
    2968             : enum class Caller
    2969             : {
    2970             :     CALLER_END_OF_LOOP,
    2971             :     CALLER_IN_LOOP,
    2972             : };
    2973             : }
    2974             : 
    2975             : /** \brief Call a user-provided function to operate on an array chunk by chunk.
    2976             :  *
    2977             :  * This method is to be used when doing operations on an array, or a subset of
    2978             :  * it, in a chunk by chunk way.
    2979             :  *
    2980             :  * @param arrayStartIdx Values representing the starting index to use
    2981             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2982             :  *                      Array of GetDimensionCount() values. Must not be
    2983             :  *                      nullptr, unless for a zero-dimensional array.
    2984             :  *
    2985             :  * @param count         Values representing the number of values to use in
    2986             :  *                      each dimension.
    2987             :  *                      Array of GetDimensionCount() values. Must not be
    2988             :  *                      nullptr, unless for a zero-dimensional array.
    2989             :  *
    2990             :  * @param chunkSize     Values representing the chunk size in each dimension.
    2991             :  *                      Might typically the output of GetProcessingChunkSize().
    2992             :  *                      Array of GetDimensionCount() values. Must not be
    2993             :  *                      nullptr, unless for a zero-dimensional array.
    2994             :  *
    2995             :  * @param pfnFunc       User-provided function of type FuncProcessPerChunkType.
    2996             :  *                      Must NOT be nullptr.
    2997             :  *
    2998             :  * @param pUserData     Pointer to pass as the value of the pUserData argument
    2999             :  * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
    3000             :  *
    3001             :  * @return true in case of success.
    3002             :  */
    3003          58 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
    3004             :                                           const GUInt64 *count,
    3005             :                                           const size_t *chunkSize,
    3006             :                                           FuncProcessPerChunkType pfnFunc,
    3007             :                                           void *pUserData)
    3008             : {
    3009          58 :     const auto &dims = GetDimensions();
    3010          58 :     if (dims.empty())
    3011             :     {
    3012           2 :         return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
    3013             :     }
    3014             : 
    3015             :     // Sanity check
    3016          56 :     size_t nTotalChunkSize = 1;
    3017         146 :     for (size_t i = 0; i < dims.size(); i++)
    3018             :     {
    3019          97 :         const auto nSizeThisDim(dims[i]->GetSize());
    3020          97 :         if (count[i] == 0 || count[i] > nSizeThisDim ||
    3021          95 :             arrayStartIdx[i] > nSizeThisDim - count[i])
    3022             :         {
    3023           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    3024             :                      "Inconsistent arrayStartIdx[] / count[] values "
    3025             :                      "regarding array size");
    3026           4 :             return false;
    3027             :         }
    3028         184 :         if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
    3029          91 :             chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
    3030             :         {
    3031           3 :             CPLError(CE_Failure, CPLE_AppDefined,
    3032             :                      "Inconsistent chunkSize[] values");
    3033           3 :             return false;
    3034             :         }
    3035          90 :         nTotalChunkSize *= chunkSize[i];
    3036             :     }
    3037             : 
    3038          49 :     size_t dimIdx = 0;
    3039          98 :     std::vector<GUInt64> chunkArrayStartIdx(dims.size());
    3040          98 :     std::vector<size_t> chunkCount(dims.size());
    3041             : 
    3042             :     struct Stack
    3043             :     {
    3044             :         GUInt64 nBlockCounter = 0;
    3045             :         GUInt64 nBlocksMinusOne = 0;
    3046             :         size_t first_count = 0;  // only used if nBlocks > 1
    3047             :         Caller return_point = Caller::CALLER_END_OF_LOOP;
    3048             :     };
    3049             : 
    3050          98 :     std::vector<Stack> stack(dims.size());
    3051          49 :     GUInt64 iCurChunk = 0;
    3052          49 :     GUInt64 nChunkCount = 1;
    3053         138 :     for (size_t i = 0; i < dims.size(); i++)
    3054             :     {
    3055          89 :         const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
    3056          89 :         const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
    3057          89 :         stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
    3058          89 :         nChunkCount *= 1 + stack[i].nBlocksMinusOne;
    3059          89 :         if (stack[i].nBlocksMinusOne == 0)
    3060             :         {
    3061          84 :             chunkArrayStartIdx[i] = arrayStartIdx[i];
    3062          84 :             chunkCount[i] = static_cast<size_t>(count[i]);
    3063             :         }
    3064             :         else
    3065             :         {
    3066           5 :             stack[i].first_count = static_cast<size_t>(
    3067           5 :                 (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
    3068             :         }
    3069             :     }
    3070             : 
    3071          49 : lbl_next_depth:
    3072         248 :     if (dimIdx == dims.size())
    3073             :     {
    3074          82 :         ++iCurChunk;
    3075          82 :         if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
    3076             :                      iCurChunk, nChunkCount, pUserData))
    3077             :         {
    3078           0 :             return false;
    3079             :         }
    3080             :     }
    3081             :     else
    3082             :     {
    3083         166 :         if (stack[dimIdx].nBlocksMinusOne != 0)
    3084             :         {
    3085          11 :             stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
    3086          11 :             chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
    3087          11 :             chunkCount[dimIdx] = stack[dimIdx].first_count;
    3088          11 :             stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
    3089             :             while (true)
    3090             :             {
    3091          33 :                 dimIdx++;
    3092          33 :                 goto lbl_next_depth;
    3093          33 :             lbl_return_to_caller_in_loop:
    3094          33 :                 --stack[dimIdx].nBlockCounter;
    3095          33 :                 if (stack[dimIdx].nBlockCounter == 0)
    3096          11 :                     break;
    3097          22 :                 chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    3098          22 :                 chunkCount[dimIdx] = chunkSize[dimIdx];
    3099             :             }
    3100             : 
    3101          11 :             chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    3102          22 :             chunkCount[dimIdx] =
    3103          11 :                 static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
    3104          11 :                                     chunkArrayStartIdx[dimIdx]);
    3105          11 :             stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
    3106             :         }
    3107         166 :         dimIdx++;
    3108         166 :         goto lbl_next_depth;
    3109         166 :     lbl_return_to_caller_end_of_loop:
    3110         166 :         if (dimIdx == 0)
    3111          49 :             goto end;
    3112             :     }
    3113             : 
    3114         199 :     assert(dimIdx > 0);
    3115         199 :     dimIdx--;
    3116             :     // cppcheck-suppress negativeContainerIndex
    3117         199 :     switch (stack[dimIdx].return_point)
    3118             :     {
    3119         166 :         case Caller::CALLER_END_OF_LOOP:
    3120         166 :             goto lbl_return_to_caller_end_of_loop;
    3121          33 :         case Caller::CALLER_IN_LOOP:
    3122          33 :             goto lbl_return_to_caller_in_loop;
    3123             :     }
    3124          49 : end:
    3125          49 :     return true;
    3126             : }
    3127             : 
    3128             : /************************************************************************/
    3129             : /*                          GDALAttribute()                             */
    3130             : /************************************************************************/
    3131             : 
    3132             : //! @cond Doxygen_Suppress
    3133       14005 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
    3134           0 :                              CPL_UNUSED const std::string &osName)
    3135             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
    3136       14005 :     : GDALAbstractMDArray(osParentName, osName)
    3137             : #endif
    3138             : {
    3139       14005 : }
    3140             : 
    3141             : //! @endcond
    3142             : 
    3143             : /************************************************************************/
    3144             : /*                        GetDimensionSize()                            */
    3145             : /************************************************************************/
    3146             : 
    3147             : /** Return the size of the dimensions of the attribute.
    3148             :  *
    3149             :  * This will be an empty array for a scalar (single value) attribute.
    3150             :  *
    3151             :  * This is the same as the C function GDALAttributeGetDimensionsSize().
    3152             :  */
    3153         362 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
    3154             : {
    3155         362 :     const auto &dims = GetDimensions();
    3156         362 :     std::vector<GUInt64> ret;
    3157         362 :     ret.reserve(dims.size());
    3158         454 :     for (const auto &dim : dims)
    3159          92 :         ret.push_back(dim->GetSize());
    3160         362 :     return ret;
    3161             : }
    3162             : 
    3163             : /************************************************************************/
    3164             : /*                            GDALRawResult()                           */
    3165             : /************************************************************************/
    3166             : 
    3167             : //! @cond Doxygen_Suppress
    3168         149 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
    3169         149 :                              size_t nEltCount)
    3170         298 :     : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
    3171         149 :       m_raw(raw)
    3172             : {
    3173         149 : }
    3174             : 
    3175             : //! @endcond
    3176             : 
    3177             : /************************************************************************/
    3178             : /*                            GDALRawResult()                           */
    3179             : /************************************************************************/
    3180             : 
    3181             : /** Move constructor. */
    3182           0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
    3183           0 :     : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
    3184           0 :       m_nSize(other.m_nSize), m_raw(other.m_raw)
    3185             : {
    3186           0 :     other.m_nEltCount = 0;
    3187           0 :     other.m_nSize = 0;
    3188           0 :     other.m_raw = nullptr;
    3189           0 : }
    3190             : 
    3191             : /************************************************************************/
    3192             : /*                               FreeMe()                               */
    3193             : /************************************************************************/
    3194             : 
    3195         149 : void GDALRawResult::FreeMe()
    3196             : {
    3197         149 :     if (m_raw && m_dt.NeedsFreeDynamicMemory())
    3198             :     {
    3199          47 :         GByte *pabyPtr = m_raw;
    3200          47 :         const auto nDTSize(m_dt.GetSize());
    3201          94 :         for (size_t i = 0; i < m_nEltCount; ++i)
    3202             :         {
    3203          47 :             m_dt.FreeDynamicMemory(pabyPtr);
    3204          47 :             pabyPtr += nDTSize;
    3205             :         }
    3206             :     }
    3207         149 :     VSIFree(m_raw);
    3208         149 : }
    3209             : 
    3210             : /************************************************************************/
    3211             : /*                             operator=()                              */
    3212             : /************************************************************************/
    3213             : 
    3214             : /** Move assignment. */
    3215           0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
    3216             : {
    3217           0 :     FreeMe();
    3218           0 :     m_dt = std::move(other.m_dt);
    3219           0 :     m_nEltCount = other.m_nEltCount;
    3220           0 :     m_nSize = other.m_nSize;
    3221           0 :     m_raw = other.m_raw;
    3222           0 :     other.m_nEltCount = 0;
    3223           0 :     other.m_nSize = 0;
    3224           0 :     other.m_raw = nullptr;
    3225           0 :     return *this;
    3226             : }
    3227             : 
    3228             : /************************************************************************/
    3229             : /*                         ~GDALRawResult()                             */
    3230             : /************************************************************************/
    3231             : 
    3232             : /** Destructor. */
    3233         149 : GDALRawResult::~GDALRawResult()
    3234             : {
    3235         149 :     FreeMe();
    3236         149 : }
    3237             : 
    3238             : /************************************************************************/
    3239             : /*                            StealData()                               */
    3240             : /************************************************************************/
    3241             : 
    3242             : //! @cond Doxygen_Suppress
    3243             : /** Return buffer to caller which becomes owner of it.
    3244             :  * Only to be used by GDALAttributeReadAsRaw().
    3245             :  */
    3246           6 : GByte *GDALRawResult::StealData()
    3247             : {
    3248           6 :     GByte *ret = m_raw;
    3249           6 :     m_raw = nullptr;
    3250           6 :     m_nEltCount = 0;
    3251           6 :     m_nSize = 0;
    3252           6 :     return ret;
    3253             : }
    3254             : 
    3255             : //! @endcond
    3256             : 
    3257             : /************************************************************************/
    3258             : /*                             ReadAsRaw()                              */
    3259             : /************************************************************************/
    3260             : 
    3261             : /** Return the raw value of an attribute.
    3262             :  *
    3263             :  *
    3264             :  * This is the same as the C function GDALAttributeReadAsRaw().
    3265             :  */
    3266         149 : GDALRawResult GDALAttribute::ReadAsRaw() const
    3267             : {
    3268         149 :     const auto nEltCount(GetTotalElementsCount());
    3269         149 :     const auto &dt(GetDataType());
    3270         149 :     const auto nDTSize(dt.GetSize());
    3271             :     GByte *res = static_cast<GByte *>(
    3272         149 :         VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
    3273         149 :     if (!res)
    3274           0 :         return GDALRawResult(nullptr, dt, 0);
    3275         149 :     const auto &dims = GetDimensions();
    3276         149 :     const auto nDims = GetDimensionCount();
    3277         298 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3278         298 :     std::vector<size_t> count(1 + nDims);
    3279         168 :     for (size_t i = 0; i < nDims; i++)
    3280             :     {
    3281          19 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3282             :     }
    3283         149 :     if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
    3284         149 :               &res[0], static_cast<size_t>(nEltCount * nDTSize)))
    3285             :     {
    3286           0 :         VSIFree(res);
    3287           0 :         return GDALRawResult(nullptr, dt, 0);
    3288             :     }
    3289         149 :     return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
    3290             : }
    3291             : 
    3292             : /************************************************************************/
    3293             : /*                            ReadAsString()                            */
    3294             : /************************************************************************/
    3295             : 
    3296             : /** Return the value of an attribute as a string.
    3297             :  *
    3298             :  * The returned string should not be freed, and its lifetime does not
    3299             :  * excess a next call to ReadAsString() on the same object, or the deletion
    3300             :  * of the object itself.
    3301             :  *
    3302             :  * This function will only return the first element if there are several.
    3303             :  *
    3304             :  * This is the same as the C function GDALAttributeReadAsString()
    3305             :  *
    3306             :  * @return a string, or nullptr.
    3307             :  */
    3308        1303 : const char *GDALAttribute::ReadAsString() const
    3309             : {
    3310        1303 :     const auto nDims = GetDimensionCount();
    3311        2606 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3312        2606 :     std::vector<size_t> count(1 + nDims, 1);
    3313        1303 :     char *szRet = nullptr;
    3314        1303 :     if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
    3315        1303 :               GDALExtendedDataType::CreateString(), &szRet, &szRet,
    3316        3908 :               sizeof(szRet)) ||
    3317        1302 :         szRet == nullptr)
    3318             :     {
    3319           4 :         return nullptr;
    3320             :     }
    3321        1299 :     m_osCachedVal = szRet;
    3322        1299 :     CPLFree(szRet);
    3323        1299 :     return m_osCachedVal.c_str();
    3324             : }
    3325             : 
    3326             : /************************************************************************/
    3327             : /*                            ReadAsInt()                               */
    3328             : /************************************************************************/
    3329             : 
    3330             : /** Return the value of an attribute as a integer.
    3331             :  *
    3332             :  * This function will only return the first element if there are several.
    3333             :  *
    3334             :  * It can fail if its value can not be converted to integer.
    3335             :  *
    3336             :  * This is the same as the C function GDALAttributeReadAsInt()
    3337             :  *
    3338             :  * @return a integer, or INT_MIN in case of error.
    3339             :  */
    3340         218 : int GDALAttribute::ReadAsInt() const
    3341             : {
    3342         218 :     const auto nDims = GetDimensionCount();
    3343         436 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3344         218 :     std::vector<size_t> count(1 + nDims, 1);
    3345         218 :     int nRet = INT_MIN;
    3346         218 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3347         436 :          GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
    3348         436 :     return nRet;
    3349             : }
    3350             : 
    3351             : /************************************************************************/
    3352             : /*                            ReadAsInt64()                             */
    3353             : /************************************************************************/
    3354             : 
    3355             : /** Return the value of an attribute as an int64_t.
    3356             :  *
    3357             :  * This function will only return the first element if there are several.
    3358             :  *
    3359             :  * It can fail if its value can not be converted to long.
    3360             :  *
    3361             :  * This is the same as the C function GDALAttributeReadAsInt64()
    3362             :  *
    3363             :  * @return an int64_t, or INT64_MIN in case of error.
    3364             :  */
    3365          54 : int64_t GDALAttribute::ReadAsInt64() const
    3366             : {
    3367          54 :     const auto nDims = GetDimensionCount();
    3368         108 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3369          54 :     std::vector<size_t> count(1 + nDims, 1);
    3370          54 :     int64_t nRet = INT64_MIN;
    3371          54 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3372         108 :          GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
    3373         108 :     return nRet;
    3374             : }
    3375             : 
    3376             : /************************************************************************/
    3377             : /*                            ReadAsDouble()                            */
    3378             : /************************************************************************/
    3379             : 
    3380             : /** Return the value of an attribute as a double.
    3381             :  *
    3382             :  * This function will only return the first element if there are several.
    3383             :  *
    3384             :  * It can fail if its value can not be converted to double.
    3385             :  *
    3386             :  * This is the same as the C function GDALAttributeReadAsInt()
    3387             :  *
    3388             :  * @return a double value.
    3389             :  */
    3390         339 : double GDALAttribute::ReadAsDouble() const
    3391             : {
    3392         339 :     const auto nDims = GetDimensionCount();
    3393         678 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3394         339 :     std::vector<size_t> count(1 + nDims, 1);
    3395         339 :     double dfRet = 0;
    3396         339 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3397         339 :          GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
    3398         339 :          sizeof(dfRet));
    3399         678 :     return dfRet;
    3400             : }
    3401             : 
    3402             : /************************************************************************/
    3403             : /*                          ReadAsStringArray()                         */
    3404             : /************************************************************************/
    3405             : 
    3406             : /** Return the value of an attribute as an array of strings.
    3407             :  *
    3408             :  * This is the same as the C function GDALAttributeReadAsStringArray()
    3409             :  */
    3410         104 : CPLStringList GDALAttribute::ReadAsStringArray() const
    3411             : {
    3412         104 :     const auto nElts = GetTotalElementsCount();
    3413         104 :     if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
    3414           0 :         return CPLStringList();
    3415             :     char **papszList = static_cast<char **>(
    3416         104 :         VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
    3417         104 :     const auto &dims = GetDimensions();
    3418         104 :     const auto nDims = GetDimensionCount();
    3419         208 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3420         208 :     std::vector<size_t> count(1 + nDims);
    3421         157 :     for (size_t i = 0; i < nDims; i++)
    3422             :     {
    3423          53 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3424             :     }
    3425         104 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3426         104 :          GDALExtendedDataType::CreateString(), papszList, papszList,
    3427         104 :          sizeof(char *) * static_cast<int>(nElts));
    3428         269 :     for (int i = 0; i < static_cast<int>(nElts); i++)
    3429             :     {
    3430         165 :         if (papszList[i] == nullptr)
    3431          13 :             papszList[i] = CPLStrdup("");
    3432             :     }
    3433         104 :     return CPLStringList(papszList);
    3434             : }
    3435             : 
    3436             : /************************************************************************/
    3437             : /*                          ReadAsIntArray()                            */
    3438             : /************************************************************************/
    3439             : 
    3440             : /** Return the value of an attribute as an array of integers.
    3441             :  *
    3442             :  * This is the same as the C function GDALAttributeReadAsIntArray().
    3443             :  */
    3444          15 : std::vector<int> GDALAttribute::ReadAsIntArray() const
    3445             : {
    3446          15 :     const auto nElts = GetTotalElementsCount();
    3447             : #if SIZEOF_VOIDP == 4
    3448             :     if (nElts > static_cast<size_t>(nElts))
    3449             :         return {};
    3450             : #endif
    3451          15 :     std::vector<int> res(static_cast<size_t>(nElts));
    3452          15 :     const auto &dims = GetDimensions();
    3453          15 :     const auto nDims = GetDimensionCount();
    3454          30 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3455          30 :     std::vector<size_t> count(1 + nDims);
    3456          32 :     for (size_t i = 0; i < nDims; i++)
    3457             :     {
    3458          17 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3459             :     }
    3460          15 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3461          30 :          GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
    3462          15 :          res.size() * sizeof(res[0]));
    3463          30 :     return res;
    3464             : }
    3465             : 
    3466             : /************************************************************************/
    3467             : /*                          ReadAsInt64Array()                          */
    3468             : /************************************************************************/
    3469             : 
    3470             : /** Return the value of an attribute as an array of int64_t.
    3471             :  *
    3472             :  * This is the same as the C function GDALAttributeReadAsInt64Array().
    3473             :  */
    3474          38 : std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
    3475             : {
    3476          38 :     const auto nElts = GetTotalElementsCount();
    3477             : #if SIZEOF_VOIDP == 4
    3478             :     if (nElts > static_cast<size_t>(nElts))
    3479             :         return {};
    3480             : #endif
    3481          38 :     std::vector<int64_t> res(static_cast<size_t>(nElts));
    3482          38 :     const auto &dims = GetDimensions();
    3483          38 :     const auto nDims = GetDimensionCount();
    3484          76 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3485          76 :     std::vector<size_t> count(1 + nDims);
    3486          76 :     for (size_t i = 0; i < nDims; i++)
    3487             :     {
    3488          38 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3489             :     }
    3490          38 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3491          76 :          GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
    3492          38 :          res.size() * sizeof(res[0]));
    3493          76 :     return res;
    3494             : }
    3495             : 
    3496             : /************************************************************************/
    3497             : /*                         ReadAsDoubleArray()                          */
    3498             : /************************************************************************/
    3499             : 
    3500             : /** Return the value of an attribute as an array of double.
    3501             :  *
    3502             :  * This is the same as the C function GDALAttributeReadAsDoubleArray().
    3503             :  */
    3504          87 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
    3505             : {
    3506          87 :     const auto nElts = GetTotalElementsCount();
    3507             : #if SIZEOF_VOIDP == 4
    3508             :     if (nElts > static_cast<size_t>(nElts))
    3509             :         return {};
    3510             : #endif
    3511          87 :     std::vector<double> res(static_cast<size_t>(nElts));
    3512          87 :     const auto &dims = GetDimensions();
    3513          87 :     const auto nDims = GetDimensionCount();
    3514         174 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3515         174 :     std::vector<size_t> count(1 + nDims);
    3516         158 :     for (size_t i = 0; i < nDims; i++)
    3517             :     {
    3518          71 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3519             :     }
    3520          87 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3521         174 :          GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
    3522          87 :          res.size() * sizeof(res[0]));
    3523         174 :     return res;
    3524             : }
    3525             : 
    3526             : /************************************************************************/
    3527             : /*                               Write()                                */
    3528             : /************************************************************************/
    3529             : 
    3530             : /** Write an attribute from raw values expressed in GetDataType()
    3531             :  *
    3532             :  * The values should be provided in the type of GetDataType() and there should
    3533             :  * be exactly GetTotalElementsCount() of them.
    3534             :  * If GetDataType() is a string, each value should be a char* pointer.
    3535             :  *
    3536             :  * This is the same as the C function GDALAttributeWriteRaw().
    3537             :  *
    3538             :  * @param pabyValue Buffer of nLen bytes.
    3539             :  * @param nLen Size of pabyValue in bytes. Should be equal to
    3540             :  *             GetTotalElementsCount() * GetDataType().GetSize()
    3541             :  * @return true in case of success.
    3542             :  */
    3543          91 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
    3544             : {
    3545          91 :     if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
    3546             :     {
    3547           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3548             :                  "Length is not of expected value");
    3549           0 :         return false;
    3550             :     }
    3551          91 :     const auto &dims = GetDimensions();
    3552          91 :     const auto nDims = GetDimensionCount();
    3553         182 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3554         182 :     std::vector<size_t> count(1 + nDims);
    3555         114 :     for (size_t i = 0; i < nDims; i++)
    3556             :     {
    3557          23 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3558             :     }
    3559          91 :     return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
    3560          91 :                  pabyValue, pabyValue, nLen);
    3561             : }
    3562             : 
    3563             : /************************************************************************/
    3564             : /*                               Write()                                */
    3565             : /************************************************************************/
    3566             : 
    3567             : /** Write an attribute from a string value.
    3568             :  *
    3569             :  * Type conversion will be performed if needed. If the attribute contains
    3570             :  * multiple values, only the first one will be updated.
    3571             :  *
    3572             :  * This is the same as the C function GDALAttributeWriteString().
    3573             :  *
    3574             :  * @param pszValue Pointer to a string.
    3575             :  * @return true in case of success.
    3576             :  */
    3577         304 : bool GDALAttribute::Write(const char *pszValue)
    3578             : {
    3579         304 :     const auto nDims = GetDimensionCount();
    3580         608 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3581         304 :     std::vector<size_t> count(1 + nDims, 1);
    3582         304 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3583         608 :                  GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
    3584         608 :                  sizeof(pszValue));
    3585             : }
    3586             : 
    3587             : /************************************************************************/
    3588             : /*                              WriteInt()                              */
    3589             : /************************************************************************/
    3590             : 
    3591             : /** Write an attribute from a integer value.
    3592             :  *
    3593             :  * Type conversion will be performed if needed. If the attribute contains
    3594             :  * multiple values, only the first one will be updated.
    3595             :  *
    3596             :  * This is the same as the C function GDALAttributeWriteInt().
    3597             :  *
    3598             :  * @param nVal Value.
    3599             :  * @return true in case of success.
    3600             :  */
    3601          22 : bool GDALAttribute::WriteInt(int nVal)
    3602             : {
    3603          22 :     const auto nDims = GetDimensionCount();
    3604          44 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3605          22 :     std::vector<size_t> count(1 + nDims, 1);
    3606          22 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3607          44 :                  GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
    3608          44 :                  sizeof(nVal));
    3609             : }
    3610             : 
    3611             : /************************************************************************/
    3612             : /*                              WriteInt64()                             */
    3613             : /************************************************************************/
    3614             : 
    3615             : /** Write an attribute from an int64_t value.
    3616             :  *
    3617             :  * Type conversion will be performed if needed. If the attribute contains
    3618             :  * multiple values, only the first one will be updated.
    3619             :  *
    3620             :  * This is the same as the C function GDALAttributeWriteInt().
    3621             :  *
    3622             :  * @param nVal Value.
    3623             :  * @return true in case of success.
    3624             :  */
    3625          11 : bool GDALAttribute::WriteInt64(int64_t nVal)
    3626             : {
    3627          11 :     const auto nDims = GetDimensionCount();
    3628          22 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3629          11 :     std::vector<size_t> count(1 + nDims, 1);
    3630          11 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3631          22 :                  GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
    3632          22 :                  sizeof(nVal));
    3633             : }
    3634             : 
    3635             : /************************************************************************/
    3636             : /*                                Write()                               */
    3637             : /************************************************************************/
    3638             : 
    3639             : /** Write an attribute from a double value.
    3640             :  *
    3641             :  * Type conversion will be performed if needed. If the attribute contains
    3642             :  * multiple values, only the first one will be updated.
    3643             :  *
    3644             :  * This is the same as the C function GDALAttributeWriteDouble().
    3645             :  *
    3646             :  * @param dfVal Value.
    3647             :  * @return true in case of success.
    3648             :  */
    3649          36 : bool GDALAttribute::Write(double dfVal)
    3650             : {
    3651          36 :     const auto nDims = GetDimensionCount();
    3652          72 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3653          36 :     std::vector<size_t> count(1 + nDims, 1);
    3654          36 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3655          72 :                  GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
    3656          72 :                  sizeof(dfVal));
    3657             : }
    3658             : 
    3659             : /************************************************************************/
    3660             : /*                                Write()                               */
    3661             : /************************************************************************/
    3662             : 
    3663             : /** Write an attribute from an array of strings.
    3664             :  *
    3665             :  * Type conversion will be performed if needed.
    3666             :  *
    3667             :  * Exactly GetTotalElementsCount() strings must be provided
    3668             :  *
    3669             :  * This is the same as the C function GDALAttributeWriteStringArray().
    3670             :  *
    3671             :  * @param vals Array of strings.
    3672             :  * @return true in case of success.
    3673             :  */
    3674           8 : bool GDALAttribute::Write(CSLConstList vals)
    3675             : {
    3676           8 :     if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
    3677             :     {
    3678           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3679           1 :         return false;
    3680             :     }
    3681           7 :     const auto nDims = GetDimensionCount();
    3682          14 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3683           7 :     std::vector<size_t> count(1 + nDims);
    3684           7 :     const auto &dims = GetDimensions();
    3685          15 :     for (size_t i = 0; i < nDims; i++)
    3686           8 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3687           7 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3688           7 :                  GDALExtendedDataType::CreateString(), vals, vals,
    3689          14 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
    3690             : }
    3691             : 
    3692             : /************************************************************************/
    3693             : /*                                Write()                               */
    3694             : /************************************************************************/
    3695             : 
    3696             : /** Write an attribute from an array of int.
    3697             :  *
    3698             :  * Type conversion will be performed if needed.
    3699             :  *
    3700             :  * Exactly GetTotalElementsCount() strings must be provided
    3701             :  *
    3702             :  * This is the same as the C function GDALAttributeWriteIntArray()
    3703             :  *
    3704             :  * @param vals Array of int.
    3705             :  * @param nVals Should be equal to GetTotalElementsCount().
    3706             :  * @return true in case of success.
    3707             :  */
    3708           9 : bool GDALAttribute::Write(const int *vals, size_t nVals)
    3709             : {
    3710           9 :     if (nVals != GetTotalElementsCount())
    3711             :     {
    3712           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3713           1 :         return false;
    3714             :     }
    3715           8 :     const auto nDims = GetDimensionCount();
    3716          16 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3717           8 :     std::vector<size_t> count(1 + nDims);
    3718           8 :     const auto &dims = GetDimensions();
    3719          16 :     for (size_t i = 0; i < nDims; i++)
    3720           8 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3721           8 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3722           8 :                  GDALExtendedDataType::Create(GDT_Int32), vals, vals,
    3723          16 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
    3724             : }
    3725             : 
    3726             : /************************************************************************/
    3727             : /*                                Write()                               */
    3728             : /************************************************************************/
    3729             : 
    3730             : /** Write an attribute from an array of int64_t.
    3731             :  *
    3732             :  * Type conversion will be performed if needed.
    3733             :  *
    3734             :  * Exactly GetTotalElementsCount() strings must be provided
    3735             :  *
    3736             :  * This is the same as the C function GDALAttributeWriteLongArray()
    3737             :  *
    3738             :  * @param vals Array of int64_t.
    3739             :  * @param nVals Should be equal to GetTotalElementsCount().
    3740             :  * @return true in case of success.
    3741             :  */
    3742          10 : bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
    3743             : {
    3744          10 :     if (nVals != GetTotalElementsCount())
    3745             :     {
    3746           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3747           0 :         return false;
    3748             :     }
    3749          10 :     const auto nDims = GetDimensionCount();
    3750          20 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3751          10 :     std::vector<size_t> count(1 + nDims);
    3752          10 :     const auto &dims = GetDimensions();
    3753          20 :     for (size_t i = 0; i < nDims; i++)
    3754          10 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3755          10 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3756          10 :                  GDALExtendedDataType::Create(GDT_Int64), vals, vals,
    3757          10 :                  static_cast<size_t>(GetTotalElementsCount()) *
    3758          10 :                      sizeof(int64_t));
    3759             : }
    3760             : 
    3761             : /************************************************************************/
    3762             : /*                                Write()                               */
    3763             : /************************************************************************/
    3764             : 
    3765             : /** Write an attribute from an array of double.
    3766             :  *
    3767             :  * Type conversion will be performed if needed.
    3768             :  *
    3769             :  * Exactly GetTotalElementsCount() strings must be provided
    3770             :  *
    3771             :  * This is the same as the C function GDALAttributeWriteDoubleArray()
    3772             :  *
    3773             :  * @param vals Array of double.
    3774             :  * @param nVals Should be equal to GetTotalElementsCount().
    3775             :  * @return true in case of success.
    3776             :  */
    3777           7 : bool GDALAttribute::Write(const double *vals, size_t nVals)
    3778             : {
    3779           7 :     if (nVals != GetTotalElementsCount())
    3780             :     {
    3781           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3782           1 :         return false;
    3783             :     }
    3784           6 :     const auto nDims = GetDimensionCount();
    3785          12 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3786           6 :     std::vector<size_t> count(1 + nDims);
    3787           6 :     const auto &dims = GetDimensions();
    3788          13 :     for (size_t i = 0; i < nDims; i++)
    3789           7 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3790           6 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3791           6 :                  GDALExtendedDataType::Create(GDT_Float64), vals, vals,
    3792          12 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
    3793             : }
    3794             : 
    3795             : /************************************************************************/
    3796             : /*                           GDALMDArray()                              */
    3797             : /************************************************************************/
    3798             : 
    3799             : //! @cond Doxygen_Suppress
    3800        6249 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
    3801             :                          CPL_UNUSED const std::string &osName,
    3802           0 :                          const std::string &osContext)
    3803             :     :
    3804             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
    3805             :       GDALAbstractMDArray(osParentName, osName),
    3806             : #endif
    3807        6249 :       m_osContext(osContext)
    3808             : {
    3809        6249 : }
    3810             : 
    3811             : //! @endcond
    3812             : 
    3813             : /************************************************************************/
    3814             : /*                           GetTotalCopyCost()                         */
    3815             : /************************************************************************/
    3816             : 
    3817             : /** Return a total "cost" to copy the array.
    3818             :  *
    3819             :  * Used as a parameter for CopyFrom()
    3820             :  */
    3821          43 : GUInt64 GDALMDArray::GetTotalCopyCost() const
    3822             : {
    3823          86 :     return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
    3824          86 :            GetTotalElementsCount() * GetDataType().GetSize();
    3825             : }
    3826             : 
    3827             : /************************************************************************/
    3828             : /*                       CopyFromAllExceptValues()                      */
    3829             : /************************************************************************/
    3830             : 
    3831             : //! @cond Doxygen_Suppress
    3832             : 
    3833         144 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
    3834             :                                           bool bStrict, GUInt64 &nCurCost,
    3835             :                                           const GUInt64 nTotalCost,
    3836             :                                           GDALProgressFunc pfnProgress,
    3837             :                                           void *pProgressData)
    3838             : {
    3839             :     // Nodata setting must be one of the first things done for TileDB
    3840         144 :     const void *pNoData = poSrcArray->GetRawNoDataValue();
    3841         144 :     if (pNoData && poSrcArray->GetDataType() == GetDataType())
    3842             :     {
    3843          13 :         SetRawNoDataValue(pNoData);
    3844             :     }
    3845             : 
    3846         144 :     const bool bThisIsUnscaledArray =
    3847         144 :         dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
    3848         288 :     auto attrs = poSrcArray->GetAttributes();
    3849         191 :     for (const auto &attr : attrs)
    3850             :     {
    3851          47 :         const auto &osAttrName = attr->GetName();
    3852          47 :         if (bThisIsUnscaledArray)
    3853             :         {
    3854           6 :             if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
    3855           7 :                 osAttrName == "valid_min" || osAttrName == "valid_max" ||
    3856           1 :                 osAttrName == "valid_range")
    3857             :             {
    3858           1 :                 continue;
    3859             :             }
    3860             :         }
    3861             : 
    3862          46 :         auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
    3863          92 :                                        attr->GetDataType());
    3864          46 :         if (!dstAttr)
    3865             :         {
    3866           0 :             if (bStrict)
    3867           0 :                 return false;
    3868           0 :             continue;
    3869             :         }
    3870          46 :         auto raw = attr->ReadAsRaw();
    3871          46 :         if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
    3872           0 :             return false;
    3873             :     }
    3874         144 :     if (!attrs.empty())
    3875             :     {
    3876          26 :         nCurCost += attrs.size() * GDALAttribute::COPY_COST;
    3877          46 :         if (pfnProgress &&
    3878          20 :             !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    3879           0 :             return false;
    3880             :     }
    3881             : 
    3882         144 :     auto srcSRS = poSrcArray->GetSpatialRef();
    3883         144 :     if (srcSRS)
    3884             :     {
    3885          11 :         SetSpatialRef(srcSRS.get());
    3886             :     }
    3887             : 
    3888         144 :     const std::string &osUnit(poSrcArray->GetUnit());
    3889         144 :     if (!osUnit.empty())
    3890             :     {
    3891          18 :         SetUnit(osUnit);
    3892             :     }
    3893             : 
    3894         144 :     bool bGotValue = false;
    3895         144 :     GDALDataType eOffsetStorageType = GDT_Unknown;
    3896             :     const double dfOffset =
    3897         144 :         poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
    3898         144 :     if (bGotValue)
    3899             :     {
    3900           3 :         SetOffset(dfOffset, eOffsetStorageType);
    3901             :     }
    3902             : 
    3903         144 :     bGotValue = false;
    3904         144 :     GDALDataType eScaleStorageType = GDT_Unknown;
    3905         144 :     const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
    3906         144 :     if (bGotValue)
    3907             :     {
    3908           3 :         SetScale(dfScale, eScaleStorageType);
    3909             :     }
    3910             : 
    3911         144 :     return true;
    3912             : }
    3913             : 
    3914             : //! @endcond
    3915             : 
    3916             : /************************************************************************/
    3917             : /*                               CopyFrom()                             */
    3918             : /************************************************************************/
    3919             : 
    3920             : /** Copy the content of an array into a new (generally empty) array.
    3921             :  *
    3922             :  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
    3923             :  *                   of some output drivers this is not recommended)
    3924             :  * @param poSrcArray Source array. Should NOT be nullptr.
    3925             :  * @param bStrict Whether to enable strict mode. In strict mode, any error will
    3926             :  *                stop the copy. In relaxed mode, the copy will be attempted to
    3927             :  *                be pursued.
    3928             :  * @param nCurCost  Should be provided as a variable initially set to 0.
    3929             :  * @param nTotalCost Total cost from GetTotalCopyCost().
    3930             :  * @param pfnProgress Progress callback, or nullptr.
    3931             :  * @param pProgressData Progress user data, or nulptr.
    3932             :  *
    3933             :  * @return true in case of success (or partial success if bStrict == false).
    3934             :  */
    3935          41 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
    3936             :                            const GDALMDArray *poSrcArray, bool bStrict,
    3937             :                            GUInt64 &nCurCost, const GUInt64 nTotalCost,
    3938             :                            GDALProgressFunc pfnProgress, void *pProgressData)
    3939             : {
    3940          41 :     if (pfnProgress == nullptr)
    3941           4 :         pfnProgress = GDALDummyProgress;
    3942             : 
    3943          41 :     nCurCost += GDALMDArray::COPY_COST;
    3944             : 
    3945          41 :     if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
    3946             :                                  pfnProgress, pProgressData))
    3947             :     {
    3948           0 :         return false;
    3949             :     }
    3950             : 
    3951          41 :     const auto &dims = poSrcArray->GetDimensions();
    3952          41 :     const auto nDTSize = poSrcArray->GetDataType().GetSize();
    3953          41 :     if (dims.empty())
    3954             :     {
    3955           2 :         std::vector<GByte> abyTmp(nDTSize);
    3956           2 :         if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
    3957           2 :                                GetDataType(), &abyTmp[0]) &&
    3958           2 :               Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
    3959           4 :                     &abyTmp[0])) &&
    3960             :             bStrict)
    3961             :         {
    3962           0 :             return false;
    3963             :         }
    3964           2 :         nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
    3965           2 :         if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    3966           0 :             return false;
    3967             :     }
    3968             :     else
    3969             :     {
    3970          39 :         std::vector<GUInt64> arrayStartIdx(dims.size());
    3971          39 :         std::vector<GUInt64> count(dims.size());
    3972         106 :         for (size_t i = 0; i < dims.size(); i++)
    3973             :         {
    3974          67 :             count[i] = static_cast<size_t>(dims[i]->GetSize());
    3975             :         }
    3976             : 
    3977             :         struct CopyFunc
    3978             :         {
    3979             :             GDALMDArray *poDstArray = nullptr;
    3980             :             std::vector<GByte> abyTmp{};
    3981             :             GDALProgressFunc pfnProgress = nullptr;
    3982             :             void *pProgressData = nullptr;
    3983             :             GUInt64 nCurCost = 0;
    3984             :             GUInt64 nTotalCost = 0;
    3985             :             GUInt64 nTotalBytesThisArray = 0;
    3986             :             bool bStop = false;
    3987             : 
    3988          57 :             static bool f(GDALAbstractMDArray *l_poSrcArray,
    3989             :                           const GUInt64 *chunkArrayStartIdx,
    3990             :                           const size_t *chunkCount, GUInt64 iCurChunk,
    3991             :                           GUInt64 nChunkCount, void *pUserData)
    3992             :             {
    3993          57 :                 const auto &dt(l_poSrcArray->GetDataType());
    3994          57 :                 auto data = static_cast<CopyFunc *>(pUserData);
    3995          57 :                 auto poDstArray = data->poDstArray;
    3996          57 :                 if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
    3997          57 :                                         nullptr, dt, &data->abyTmp[0]))
    3998             :                 {
    3999           0 :                     return false;
    4000             :                 }
    4001             :                 bool bRet =
    4002          57 :                     poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
    4003          57 :                                       nullptr, dt, &data->abyTmp[0]);
    4004          57 :                 if (dt.NeedsFreeDynamicMemory())
    4005             :                 {
    4006           2 :                     const auto l_nDTSize = dt.GetSize();
    4007           2 :                     GByte *ptr = &data->abyTmp[0];
    4008           2 :                     const size_t l_nDims(l_poSrcArray->GetDimensionCount());
    4009           2 :                     size_t nEltCount = 1;
    4010           4 :                     for (size_t i = 0; i < l_nDims; ++i)
    4011             :                     {
    4012           2 :                         nEltCount *= chunkCount[i];
    4013             :                     }
    4014          10 :                     for (size_t i = 0; i < nEltCount; i++)
    4015             :                     {
    4016           8 :                         dt.FreeDynamicMemory(ptr);
    4017           8 :                         ptr += l_nDTSize;
    4018             :                     }
    4019             :                 }
    4020          57 :                 if (!bRet)
    4021             :                 {
    4022           0 :                     return false;
    4023             :                 }
    4024             : 
    4025          57 :                 double dfCurCost =
    4026          57 :                     double(data->nCurCost) + double(iCurChunk) / nChunkCount *
    4027          57 :                                                  data->nTotalBytesThisArray;
    4028          57 :                 if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
    4029             :                                        data->pProgressData))
    4030             :                 {
    4031           0 :                     data->bStop = true;
    4032           0 :                     return false;
    4033             :                 }
    4034             : 
    4035          57 :                 return true;
    4036             :             }
    4037             :         };
    4038             : 
    4039          39 :         CopyFunc copyFunc;
    4040          39 :         copyFunc.poDstArray = this;
    4041          39 :         copyFunc.nCurCost = nCurCost;
    4042          39 :         copyFunc.nTotalCost = nTotalCost;
    4043          39 :         copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
    4044          39 :         copyFunc.pfnProgress = pfnProgress;
    4045          39 :         copyFunc.pProgressData = pProgressData;
    4046             :         const char *pszSwathSize =
    4047          39 :             CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
    4048             :         const size_t nMaxChunkSize =
    4049             :             pszSwathSize
    4050          39 :                 ? static_cast<size_t>(
    4051           1 :                       std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    4052           1 :                                CPLAtoGIntBig(pszSwathSize)))
    4053             :                 : static_cast<size_t>(
    4054          38 :                       std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    4055          38 :                                GDALGetCacheMax64() / 4));
    4056          39 :         const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
    4057          39 :         size_t nRealChunkSize = nDTSize;
    4058         106 :         for (const auto &nChunkSize : anChunkSizes)
    4059             :         {
    4060          67 :             nRealChunkSize *= nChunkSize;
    4061             :         }
    4062             :         try
    4063             :         {
    4064          39 :             copyFunc.abyTmp.resize(nRealChunkSize);
    4065             :         }
    4066           0 :         catch (const std::exception &)
    4067             :         {
    4068           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    4069             :                      "Cannot allocate temporary buffer");
    4070           0 :             nCurCost += copyFunc.nTotalBytesThisArray;
    4071           0 :             return false;
    4072             :         }
    4073         116 :         if (copyFunc.nTotalBytesThisArray != 0 &&
    4074          38 :             !const_cast<GDALMDArray *>(poSrcArray)
    4075          38 :                  ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
    4076             :                                    anChunkSizes.data(), CopyFunc::f,
    4077          77 :                                    &copyFunc) &&
    4078           0 :             (bStrict || copyFunc.bStop))
    4079             :         {
    4080           0 :             nCurCost += copyFunc.nTotalBytesThisArray;
    4081           0 :             return false;
    4082             :         }
    4083          39 :         nCurCost += copyFunc.nTotalBytesThisArray;
    4084             :     }
    4085             : 
    4086          41 :     return true;
    4087             : }
    4088             : 
    4089             : /************************************************************************/
    4090             : /*                         GetStructuralInfo()                          */
    4091             : /************************************************************************/
    4092             : 
    4093             : /** Return structural information on the array.
    4094             :  *
    4095             :  * This may be the compression, etc..
    4096             :  *
    4097             :  * The return value should not be freed and is valid until GDALMDArray is
    4098             :  * released or this function called again.
    4099             :  *
    4100             :  * This is the same as the C function GDALMDArrayGetStructuralInfo().
    4101             :  */
    4102          51 : CSLConstList GDALMDArray::GetStructuralInfo() const
    4103             : {
    4104          51 :     return nullptr;
    4105             : }
    4106             : 
    4107             : /************************************************************************/
    4108             : /*                          AdviseRead()                                */
    4109             : /************************************************************************/
    4110             : 
    4111             : /** Advise driver of upcoming read requests.
    4112             :  *
    4113             :  * Some GDAL drivers operate more efficiently if they know in advance what
    4114             :  * set of upcoming read requests will be made.  The AdviseRead() method allows
    4115             :  * an application to notify the driver of the region of interest.
    4116             :  *
    4117             :  * Many drivers just ignore the AdviseRead() call, but it can dramatically
    4118             :  * accelerate access via some drivers. One such case is when reading through
    4119             :  * a DAP dataset with the netCDF driver (a in-memory cache array is then created
    4120             :  * with the region of interest defined by AdviseRead())
    4121             :  *
    4122             :  * This is the same as the C function GDALMDArrayAdviseRead().
    4123             :  *
    4124             :  * @param arrayStartIdx Values representing the starting index to read
    4125             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    4126             :  *                      Array of GetDimensionCount() values.
    4127             :  *                      Can be nullptr as a synonymous for [0 for i in
    4128             :  * range(GetDimensionCount() ]
    4129             :  *
    4130             :  * @param count         Values representing the number of values to extract in
    4131             :  *                      each dimension.
    4132             :  *                      Array of GetDimensionCount() values.
    4133             :  *                      Can be nullptr as a synonymous for
    4134             :  *                      [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
    4135             :  * range(GetDimensionCount() ]
    4136             :  *
    4137             :  * @param papszOptions Driver specific options, or nullptr. Consult driver
    4138             :  * documentation.
    4139             :  *
    4140             :  * @return true in case of success (ignoring the advice is a success)
    4141             :  *
    4142             :  * @since GDAL 3.2
    4143             :  */
    4144          25 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    4145             :                              CSLConstList papszOptions) const
    4146             : {
    4147          25 :     const auto nDimCount = GetDimensionCount();
    4148          25 :     if (nDimCount == 0)
    4149           2 :         return true;
    4150             : 
    4151          46 :     std::vector<GUInt64> tmp_arrayStartIdx;
    4152          23 :     if (arrayStartIdx == nullptr)
    4153             :     {
    4154           0 :         tmp_arrayStartIdx.resize(nDimCount);
    4155           0 :         arrayStartIdx = tmp_arrayStartIdx.data();
    4156             :     }
    4157             : 
    4158          46 :     std::vector<size_t> tmp_count;
    4159          23 :     if (count == nullptr)
    4160             :     {
    4161           0 :         tmp_count.resize(nDimCount);
    4162           0 :         const auto &dims = GetDimensions();
    4163           0 :         for (size_t i = 0; i < nDimCount; i++)
    4164             :         {
    4165           0 :             const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
    4166             : #if SIZEOF_VOIDP < 8
    4167             :             if (nSize != static_cast<size_t>(nSize))
    4168             :             {
    4169             :                 CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
    4170             :                 return false;
    4171             :             }
    4172             : #endif
    4173           0 :             tmp_count[i] = static_cast<size_t>(nSize);
    4174             :         }
    4175           0 :         count = tmp_count.data();
    4176             :     }
    4177             : 
    4178          46 :     std::vector<GInt64> tmp_arrayStep;
    4179          46 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    4180          23 :     const GInt64 *arrayStep = nullptr;
    4181          23 :     const GPtrDiff_t *bufferStride = nullptr;
    4182          23 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    4183          46 :                               GDALExtendedDataType::Create(GDT_Unknown),
    4184             :                               nullptr, nullptr, 0, tmp_arrayStep,
    4185             :                               tmp_bufferStride))
    4186             :     {
    4187           1 :         return false;
    4188             :     }
    4189             : 
    4190          22 :     return IAdviseRead(arrayStartIdx, count, papszOptions);
    4191             : }
    4192             : 
    4193             : /************************************************************************/
    4194             : /*                             IAdviseRead()                            */
    4195             : /************************************************************************/
    4196             : 
    4197             : //! @cond Doxygen_Suppress
    4198           3 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
    4199             :                               CSLConstList /* papszOptions*/) const
    4200             : {
    4201           3 :     return true;
    4202             : }
    4203             : 
    4204             : //! @endcond
    4205             : 
    4206             : /************************************************************************/
    4207             : /*                            MassageName()                             */
    4208             : /************************************************************************/
    4209             : 
    4210             : //! @cond Doxygen_Suppress
    4211          32 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
    4212             : {
    4213          32 :     std::string ret;
    4214         604 :     for (const char ch : inputName)
    4215             :     {
    4216         572 :         if (!isalnum(static_cast<unsigned char>(ch)))
    4217         138 :             ret += '_';
    4218             :         else
    4219         434 :             ret += ch;
    4220             :     }
    4221          32 :     return ret;
    4222             : }
    4223             : 
    4224             : //! @endcond
    4225             : 
    4226             : /************************************************************************/
    4227             : /*                         GetCacheRootGroup()                          */
    4228             : /************************************************************************/
    4229             : 
    4230             : //! @cond Doxygen_Suppress
    4231             : std::shared_ptr<GDALGroup>
    4232        1358 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
    4233             :                                std::string &osCacheFilenameOut) const
    4234             : {
    4235        1358 :     const auto &osFilename = GetFilename();
    4236        1358 :     if (osFilename.empty())
    4237             :     {
    4238           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4239             :                  "Cannot cache an array with an empty filename");
    4240           1 :         return nullptr;
    4241             :     }
    4242             : 
    4243        1357 :     osCacheFilenameOut = osFilename + ".gmac";
    4244        1357 :     if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
    4245             :     {
    4246           0 :         const auto nPosQuestionMark = osFilename.find('?');
    4247           0 :         if (nPosQuestionMark != std::string::npos)
    4248             :         {
    4249             :             osCacheFilenameOut =
    4250           0 :                 osFilename.substr(0, nPosQuestionMark)
    4251           0 :                     .append(".gmac")
    4252           0 :                     .append(osFilename.substr(nPosQuestionMark));
    4253             :         }
    4254             :     }
    4255        1357 :     const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
    4256        1357 :     if (pszProxy != nullptr)
    4257           7 :         osCacheFilenameOut = pszProxy;
    4258             : 
    4259        1357 :     std::unique_ptr<GDALDataset> poDS;
    4260             :     VSIStatBufL sStat;
    4261        1357 :     if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
    4262             :     {
    4263          28 :         poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
    4264             :                                      GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
    4265             :                                      nullptr, nullptr, nullptr));
    4266             :     }
    4267        1357 :     if (poDS)
    4268             :     {
    4269          28 :         CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
    4270          28 :         return poDS->GetRootGroup();
    4271             :     }
    4272             : 
    4273        1329 :     if (bCanCreate)
    4274             :     {
    4275           4 :         const char *pszDrvName = "netCDF";
    4276           4 :         GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
    4277           4 :         if (poDrv == nullptr)
    4278             :         {
    4279           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
    4280             :                      pszDrvName);
    4281           0 :             return nullptr;
    4282             :         }
    4283             :         {
    4284           8 :             CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
    4285           8 :             CPLErrorStateBackuper oErrorStateBackuper;
    4286           4 :             poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
    4287             :                                                      nullptr, nullptr));
    4288             :         }
    4289           4 :         if (!poDS)
    4290             :         {
    4291           1 :             pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
    4292           1 :             if (pszProxy)
    4293             :             {
    4294           1 :                 osCacheFilenameOut = pszProxy;
    4295           1 :                 poDS.reset(poDrv->CreateMultiDimensional(
    4296             :                     osCacheFilenameOut.c_str(), nullptr, nullptr));
    4297             :             }
    4298             :         }
    4299           4 :         if (poDS)
    4300             :         {
    4301           4 :             CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
    4302           4 :             return poDS->GetRootGroup();
    4303             :         }
    4304             :         else
    4305             :         {
    4306           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4307             :                      "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
    4308             :                      "configuration option to write the cache in "
    4309             :                      "another directory",
    4310             :                      osCacheFilenameOut.c_str());
    4311             :         }
    4312             :     }
    4313             : 
    4314        1325 :     return nullptr;
    4315             : }
    4316             : 
    4317             : //! @endcond
    4318             : 
    4319             : /************************************************************************/
    4320             : /*                              Cache()                                 */
    4321             : /************************************************************************/
    4322             : 
    4323             : /** Cache the content of the array into an auxiliary filename.
    4324             :  *
    4325             :  * The main purpose of this method is to be able to cache views that are
    4326             :  * expensive to compute, such as transposed arrays.
    4327             :  *
    4328             :  * The array will be stored in a file whose name is the one of
    4329             :  * GetFilename(), with an extra .gmac extension (stands for GDAL
    4330             :  * Multidimensional Array Cache). The cache is a netCDF dataset.
    4331             :  *
    4332             :  * If the .gmac file cannot be written next to the dataset, the
    4333             :  * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
    4334             :  * directory.
    4335             :  *
    4336             :  * The GDALMDArray::Read() method will automatically use the cache when it
    4337             :  * exists. There is no timestamp checks between the source array and the cached
    4338             :  * array. If the source arrays changes, the cache must be manually deleted.
    4339             :  *
    4340             :  * This is the same as the C function GDALMDArrayCache()
    4341             :  *
    4342             :  * @note Driver implementation: optionally implemented.
    4343             :  *
    4344             :  * @param papszOptions List of options, null terminated, or NULL. Currently
    4345             :  *                     the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
    4346             :  *                     to specify the block size of the cached array.
    4347             :  * @return true in case of success.
    4348             :  */
    4349           7 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
    4350             : {
    4351          14 :     std::string osCacheFilename;
    4352          14 :     auto poRG = GetCacheRootGroup(true, osCacheFilename);
    4353           7 :     if (!poRG)
    4354           1 :         return false;
    4355             : 
    4356          12 :     const std::string osCachedArrayName(MassageName(GetFullName()));
    4357           6 :     if (poRG->OpenMDArray(osCachedArrayName))
    4358             :     {
    4359           2 :         CPLError(CE_Failure, CPLE_NotSupported,
    4360             :                  "An array with same name %s already exists in %s",
    4361             :                  osCachedArrayName.c_str(), osCacheFilename.c_str());
    4362           2 :         return false;
    4363             :     }
    4364             : 
    4365           8 :     CPLStringList aosOptions;
    4366           4 :     aosOptions.SetNameValue("COMPRESS", "DEFLATE");
    4367           4 :     const auto &aoDims = GetDimensions();
    4368           8 :     std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
    4369           4 :     if (!aoDims.empty())
    4370             :     {
    4371             :         std::string osBlockSize(
    4372           4 :             CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
    4373           4 :         if (osBlockSize.empty())
    4374             :         {
    4375           6 :             const auto anBlockSize = GetBlockSize();
    4376           3 :             int idxDim = 0;
    4377          10 :             for (auto nBlockSize : anBlockSize)
    4378             :             {
    4379           7 :                 if (idxDim > 0)
    4380           4 :                     osBlockSize += ',';
    4381           7 :                 if (nBlockSize == 0)
    4382           7 :                     nBlockSize = 256;
    4383           7 :                 nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
    4384             :                 osBlockSize +=
    4385           7 :                     std::to_string(static_cast<uint64_t>(nBlockSize));
    4386           7 :                 idxDim++;
    4387             :             }
    4388             :         }
    4389           4 :         aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
    4390             : 
    4391           4 :         int idxDim = 0;
    4392          13 :         for (const auto &poDim : aoDims)
    4393             :         {
    4394           9 :             auto poNewDim = poRG->CreateDimension(
    4395          18 :                 osCachedArrayName + '_' + std::to_string(idxDim),
    4396          18 :                 poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
    4397           9 :             if (!poNewDim)
    4398           0 :                 return false;
    4399           9 :             aoNewDims.emplace_back(poNewDim);
    4400           9 :             idxDim++;
    4401             :         }
    4402             :     }
    4403             : 
    4404           4 :     auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
    4405           8 :                                              GetDataType(), aosOptions.List());
    4406           4 :     if (!poCachedArray)
    4407             :     {
    4408           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
    4409             :                  osCachedArrayName.c_str(), osCacheFilename.c_str());
    4410           0 :         return false;
    4411             :     }
    4412             : 
    4413           4 :     GUInt64 nCost = 0;
    4414           8 :     return poCachedArray->CopyFrom(nullptr, this,
    4415             :                                    false,  // strict
    4416           4 :                                    nCost, GetTotalCopyCost(), nullptr, nullptr);
    4417             : }
    4418             : 
    4419             : /************************************************************************/
    4420             : /*                               Read()                                 */
    4421             : /************************************************************************/
    4422             : 
    4423        3741 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
    4424             :                        const GInt64 *arrayStep,         // step in elements
    4425             :                        const GPtrDiff_t *bufferStride,  // stride in elements
    4426             :                        const GDALExtendedDataType &bufferDataType,
    4427             :                        void *pDstBuffer, const void *pDstBufferAllocStart,
    4428             :                        size_t nDstBufferAllocSize) const
    4429             : {
    4430        3741 :     if (!m_bHasTriedCachedArray)
    4431             :     {
    4432        1649 :         m_bHasTriedCachedArray = true;
    4433        1649 :         if (IsCacheable())
    4434             :         {
    4435        1649 :             const auto &osFilename = GetFilename();
    4436        2804 :             if (!osFilename.empty() &&
    4437        2804 :                 !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
    4438             :             {
    4439        2290 :                 std::string osCacheFilename;
    4440        2290 :                 auto poRG = GetCacheRootGroup(false, osCacheFilename);
    4441        1145 :                 if (poRG)
    4442             :                 {
    4443             :                     const std::string osCachedArrayName(
    4444          32 :                         MassageName(GetFullName()));
    4445          16 :                     m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
    4446          16 :                     if (m_poCachedArray)
    4447             :                     {
    4448           6 :                         const auto &dims = GetDimensions();
    4449             :                         const auto &cachedDims =
    4450           6 :                             m_poCachedArray->GetDimensions();
    4451           6 :                         const size_t nDims = dims.size();
    4452             :                         bool ok =
    4453          12 :                             m_poCachedArray->GetDataType() == GetDataType() &&
    4454           6 :                             cachedDims.size() == nDims;
    4455          19 :                         for (size_t i = 0; ok && i < nDims; ++i)
    4456             :                         {
    4457          13 :                             ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
    4458             :                         }
    4459           6 :                         if (ok)
    4460             :                         {
    4461           6 :                             CPLDebug("GDAL", "Cached array for %s found in %s",
    4462             :                                      osCachedArrayName.c_str(),
    4463             :                                      osCacheFilename.c_str());
    4464             :                         }
    4465             :                         else
    4466             :                         {
    4467           0 :                             CPLError(CE_Warning, CPLE_AppDefined,
    4468             :                                      "Cached array %s in %s has incompatible "
    4469             :                                      "characteristics with current array.",
    4470             :                                      osCachedArrayName.c_str(),
    4471             :                                      osCacheFilename.c_str());
    4472           0 :                             m_poCachedArray.reset();
    4473             :                         }
    4474             :                     }
    4475             :                 }
    4476             :             }
    4477             :         }
    4478             :     }
    4479             : 
    4480        3741 :     const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
    4481        3741 :     if (!array->GetDataType().CanConvertTo(bufferDataType))
    4482             :     {
    4483           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4484             :                  "Array data type is not convertible to buffer data type");
    4485           0 :         return false;
    4486             :     }
    4487             : 
    4488        7482 :     std::vector<GInt64> tmp_arrayStep;
    4489        7482 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    4490        3741 :     if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
    4491             :                                      bufferStride, bufferDataType, pDstBuffer,
    4492             :                                      pDstBufferAllocStart, nDstBufferAllocSize,
    4493             :                                      tmp_arrayStep, tmp_bufferStride))
    4494             :     {
    4495           9 :         return false;
    4496             :     }
    4497             : 
    4498        3732 :     return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
    4499        3732 :                         bufferDataType, pDstBuffer);
    4500             : }
    4501             : 
    4502             : /************************************************************************/
    4503             : /*                          GetRootGroup()                              */
    4504             : /************************************************************************/
    4505             : 
    4506             : /** Return the root group to which this arrays belongs too.
    4507             :  *
    4508             :  * Note that arrays may be free standing and some drivers may not implement
    4509             :  * this method, hence nullptr may be returned.
    4510             :  *
    4511             :  * It is used internally by the GetResampled() method to detect if GLT
    4512             :  * orthorectification is available.
    4513             :  *
    4514             :  * @return the root group, or nullptr.
    4515             :  * @since GDAL 3.8
    4516             :  */
    4517           0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
    4518             : {
    4519           0 :     return nullptr;
    4520             : }
    4521             : 
    4522             : //! @cond Doxygen_Suppress
    4523             : 
    4524             : /************************************************************************/
    4525             : /*                       IsTransposedRequest()                          */
    4526             : /************************************************************************/
    4527             : 
    4528         689 : bool GDALMDArray::IsTransposedRequest(
    4529             :     const size_t *count,
    4530             :     const GPtrDiff_t *bufferStride) const  // stride in elements
    4531             : {
    4532             :     /*
    4533             :     For example:
    4534             :     count = [2,3,4]
    4535             :     strides = [12, 4, 1]            (2-1)*12+(3-1)*4+(4-1)*1=23   ==> row major
    4536             :     stride [12, 1, 3]            (2-1)*12+(3-1)*1+(4-1)*3=23   ==>
    4537             :     (axis[0],axis[2],axis[1]) transposition [1, 8, 2]             (2-1)*1+
    4538             :     (3-1)*8+(4-1)*2=23   ==> (axis[2],axis[1],axis[0]) transposition
    4539             :     */
    4540         689 :     const size_t nDims(GetDimensionCount());
    4541         689 :     size_t nCurStrideForRowMajorStrides = 1;
    4542         689 :     bool bRowMajorStrides = true;
    4543         689 :     size_t nElts = 1;
    4544         689 :     size_t nLastIdx = 0;
    4545        1954 :     for (size_t i = nDims; i > 0;)
    4546             :     {
    4547        1265 :         --i;
    4548        1265 :         if (bufferStride[i] < 0)
    4549           0 :             return false;
    4550        1265 :         if (static_cast<size_t>(bufferStride[i]) !=
    4551             :             nCurStrideForRowMajorStrides)
    4552             :         {
    4553         221 :             bRowMajorStrides = false;
    4554             :         }
    4555             :         // Integer overflows have already been checked in CheckReadWriteParams()
    4556        1265 :         nCurStrideForRowMajorStrides *= count[i];
    4557        1265 :         nElts *= count[i];
    4558        1265 :         nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
    4559             :     }
    4560         689 :     if (bRowMajorStrides)
    4561         540 :         return false;
    4562         149 :     return nLastIdx == nElts - 1;
    4563             : }
    4564             : 
    4565             : /************************************************************************/
    4566             : /*                   CopyToFinalBufferSameDataType()                    */
    4567             : /************************************************************************/
    4568             : 
    4569             : template <size_t N>
    4570          60 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
    4571             :                                    size_t nDims, const size_t *count,
    4572             :                                    const GPtrDiff_t *bufferStride)
    4573             : {
    4574         120 :     std::vector<size_t> anStackCount(nDims);
    4575         120 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    4576          60 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
    4577             : #if defined(__GNUC__)
    4578             : #pragma GCC diagnostic push
    4579             : #pragma GCC diagnostic ignored "-Wnull-dereference"
    4580             : #endif
    4581          60 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    4582             : #if defined(__GNUC__)
    4583             : #pragma GCC diagnostic pop
    4584             : #endif
    4585          60 :     size_t iDim = 0;
    4586             : 
    4587         749 : lbl_next_depth:
    4588         749 :     if (iDim == nDims - 1)
    4589             :     {
    4590         661 :         size_t n = count[iDim];
    4591         661 :         GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
    4592         661 :         const auto bufferStrideLastDim = bufferStride[iDim] * N;
    4593       29186 :         while (n > 0)
    4594             :         {
    4595       28525 :             --n;
    4596       28525 :             memcpy(pabyDstBuffer, pabySrcBuffer, N);
    4597       28525 :             pabyDstBuffer += bufferStrideLastDim;
    4598       28525 :             pabySrcBuffer += N;
    4599             :         }
    4600             :     }
    4601             :     else
    4602             :     {
    4603          88 :         anStackCount[iDim] = count[iDim];
    4604             :         while (true)
    4605             :         {
    4606         689 :             ++iDim;
    4607         689 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    4608         689 :             goto lbl_next_depth;
    4609         689 :         lbl_return_to_caller_in_loop:
    4610         689 :             --iDim;
    4611         689 :             --anStackCount[iDim];
    4612         689 :             if (anStackCount[iDim] == 0)
    4613          88 :                 break;
    4614         601 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
    4615             :         }
    4616             :     }
    4617         749 :     if (iDim > 0)
    4618         689 :         goto lbl_return_to_caller_in_loop;
    4619          60 : }
    4620             : 
    4621             : /************************************************************************/
    4622             : /*                        CopyToFinalBuffer()                           */
    4623             : /************************************************************************/
    4624             : 
    4625         129 : static void CopyToFinalBuffer(const void *pSrcBuffer,
    4626             :                               const GDALExtendedDataType &eSrcDataType,
    4627             :                               void *pDstBuffer,
    4628             :                               const GDALExtendedDataType &eDstDataType,
    4629             :                               size_t nDims, const size_t *count,
    4630             :                               const GPtrDiff_t *bufferStride)
    4631             : {
    4632         129 :     const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
    4633             :     // Use specialized implementation for well-known data types when no
    4634             :     // type conversion is needed
    4635         129 :     if (eSrcDataType == eDstDataType)
    4636             :     {
    4637         110 :         if (nSrcDataTypeSize == 1)
    4638             :         {
    4639          41 :             CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
    4640             :                                              count, bufferStride);
    4641          60 :             return;
    4642             :         }
    4643          69 :         else if (nSrcDataTypeSize == 2)
    4644             :         {
    4645           1 :             CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
    4646             :                                              count, bufferStride);
    4647           1 :             return;
    4648             :         }
    4649          68 :         else if (nSrcDataTypeSize == 4)
    4650             :         {
    4651          14 :             CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
    4652             :                                              count, bufferStride);
    4653          14 :             return;
    4654             :         }
    4655          54 :         else if (nSrcDataTypeSize == 8)
    4656             :         {
    4657           4 :             CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
    4658             :                                              count, bufferStride);
    4659           4 :             return;
    4660             :         }
    4661             :     }
    4662             : 
    4663          69 :     const size_t nDstDataTypeSize(eDstDataType.GetSize());
    4664         138 :     std::vector<size_t> anStackCount(nDims);
    4665         138 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    4666          69 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
    4667          69 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    4668          69 :     size_t iDim = 0;
    4669             : 
    4670         338 : lbl_next_depth:
    4671         338 :     if (iDim == nDims - 1)
    4672             :     {
    4673         328 :         GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
    4674         328 :                                          pabyDstBufferStack[iDim], eDstDataType,
    4675         328 :                                          bufferStride[iDim], count[iDim]);
    4676         328 :         pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
    4677             :     }
    4678             :     else
    4679             :     {
    4680          10 :         anStackCount[iDim] = count[iDim];
    4681             :         while (true)
    4682             :         {
    4683         269 :             ++iDim;
    4684         269 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    4685         269 :             goto lbl_next_depth;
    4686         269 :         lbl_return_to_caller_in_loop:
    4687         269 :             --iDim;
    4688         269 :             --anStackCount[iDim];
    4689         269 :             if (anStackCount[iDim] == 0)
    4690          10 :                 break;
    4691         259 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
    4692             :         }
    4693             :     }
    4694         338 :     if (iDim > 0)
    4695         269 :         goto lbl_return_to_caller_in_loop;
    4696             : }
    4697             : 
    4698             : /************************************************************************/
    4699             : /*                      TransposeLast2Dims()                            */
    4700             : /************************************************************************/
    4701             : 
    4702          19 : static bool TransposeLast2Dims(void *pDstBuffer,
    4703             :                                const GDALExtendedDataType &eDT,
    4704             :                                const size_t nDims, const size_t *count,
    4705             :                                const size_t nEltsNonLast2Dims)
    4706             : {
    4707          19 :     const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
    4708          19 :     const auto nDTSize = eDT.GetSize();
    4709             :     void *pTempBufferForLast2DimsTranspose =
    4710          19 :         VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
    4711          19 :     if (pTempBufferForLast2DimsTranspose == nullptr)
    4712           0 :         return false;
    4713             : 
    4714          19 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
    4715          58 :     for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
    4716             :     {
    4717          39 :         GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
    4718             :                         pTempBufferForLast2DimsTranspose,
    4719          39 :                         eDT.GetNumericDataType(), count[nDims - 1],
    4720          39 :                         count[nDims - 2]);
    4721          39 :         memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
    4722             :                nDTSize * nEltsLast2Dims);
    4723          39 :         pabyDstBuffer += nDTSize * nEltsLast2Dims;
    4724             :     }
    4725             : 
    4726          19 :     VSIFree(pTempBufferForLast2DimsTranspose);
    4727             : 
    4728          19 :     return true;
    4729             : }
    4730             : 
    4731             : /************************************************************************/
    4732             : /*                      ReadForTransposedRequest()                      */
    4733             : /************************************************************************/
    4734             : 
    4735             : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
    4736             : // transposed view yield to extremely poor/unusable performance. This fixes
    4737             : // this by using temporary memory to read in a contiguous buffer in a
    4738             : // row-major order, and then do the transposition to the final buffer.
    4739             : 
    4740         148 : bool GDALMDArray::ReadForTransposedRequest(
    4741             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    4742             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
    4743             :     void *pDstBuffer) const
    4744             : {
    4745         148 :     const size_t nDims(GetDimensionCount());
    4746         148 :     if (nDims == 0)
    4747             :     {
    4748           0 :         CPLAssert(false);
    4749             :         return false;  // shouldn't happen
    4750             :     }
    4751         148 :     size_t nElts = 1;
    4752         418 :     for (size_t i = 0; i < nDims; ++i)
    4753         270 :         nElts *= count[i];
    4754             : 
    4755         296 :     std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
    4756         148 :     tmpBufferStrides.back() = 1;
    4757         270 :     for (size_t i = nDims - 1; i > 0;)
    4758             :     {
    4759         122 :         --i;
    4760         122 :         tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
    4761             :     }
    4762             : 
    4763         148 :     const auto &eDT = GetDataType();
    4764         148 :     const auto nDTSize = eDT.GetSize();
    4765         277 :     if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
    4766         293 :         static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
    4767          16 :         (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
    4768             :     {
    4769             :         // Optimization of the optimization if only the last 2 dims are
    4770             :         // transposed that saves on temporary buffer allocation
    4771          23 :         const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
    4772          23 :         size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
    4773          23 :         bool bRowMajorStridesForNonLast2Dims = true;
    4774          23 :         size_t nEltsNonLast2Dims = 1;
    4775          40 :         for (size_t i = nDims - 2; i > 0;)
    4776             :         {
    4777          17 :             --i;
    4778          17 :             if (static_cast<size_t>(bufferStride[i]) !=
    4779             :                 nCurStrideForRowMajorStrides)
    4780             :             {
    4781           4 :                 bRowMajorStridesForNonLast2Dims = false;
    4782             :             }
    4783             :             // Integer overflows have already been checked in
    4784             :             // CheckReadWriteParams()
    4785          17 :             nCurStrideForRowMajorStrides *= count[i];
    4786          17 :             nEltsNonLast2Dims *= count[i];
    4787             :         }
    4788          23 :         if (bRowMajorStridesForNonLast2Dims)
    4789             :         {
    4790             :             // We read in the final buffer!
    4791          19 :             if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
    4792          19 :                        eDT, pDstBuffer))
    4793             :             {
    4794           0 :                 return false;
    4795             :             }
    4796             : 
    4797          19 :             return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
    4798          19 :                                       nEltsNonLast2Dims);
    4799             :         }
    4800             :     }
    4801             : 
    4802         129 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
    4803         129 :     if (pTempBuffer == nullptr)
    4804           0 :         return false;
    4805             : 
    4806         129 :     if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
    4807         129 :                pTempBuffer))
    4808             :     {
    4809           0 :         VSIFree(pTempBuffer);
    4810           0 :         return false;
    4811             :     }
    4812         129 :     CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
    4813             :                       count, bufferStride);
    4814             : 
    4815         129 :     if (eDT.NeedsFreeDynamicMemory())
    4816             :     {
    4817          58 :         GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
    4818         116 :         for (size_t i = 0; i < nElts; ++i)
    4819             :         {
    4820          58 :             eDT.FreeDynamicMemory(pabyPtr);
    4821          58 :             pabyPtr += nDTSize;
    4822             :         }
    4823             :     }
    4824             : 
    4825         129 :     VSIFree(pTempBuffer);
    4826         129 :     return true;
    4827             : }
    4828             : 
    4829             : /************************************************************************/
    4830             : /*               IsStepOneContiguousRowMajorOrderedSameDataType()       */
    4831             : /************************************************************************/
    4832             : 
    4833             : // Returns true if at all following conditions are met:
    4834             : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
    4835             : // defines a row-major ordered contiguous buffer.
    4836          78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
    4837             :     const size_t *count, const GInt64 *arrayStep,
    4838             :     const GPtrDiff_t *bufferStride,
    4839             :     const GDALExtendedDataType &bufferDataType) const
    4840             : {
    4841          78 :     if (bufferDataType != GetDataType())
    4842           5 :         return false;
    4843          73 :     size_t nExpectedStride = 1;
    4844         166 :     for (size_t i = GetDimensionCount(); i > 0;)
    4845             :     {
    4846          96 :         --i;
    4847          96 :         if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
    4848          94 :             static_cast<size_t>(bufferStride[i]) != nExpectedStride)
    4849             :         {
    4850           3 :             return false;
    4851             :         }
    4852          93 :         nExpectedStride *= count[i];
    4853             :     }
    4854          70 :     return true;
    4855             : }
    4856             : 
    4857             : /************************************************************************/
    4858             : /*                      ReadUsingContiguousIRead()                      */
    4859             : /************************************************************************/
    4860             : 
    4861             : // Used for example by the TileDB driver when requesting it with
    4862             : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
    4863             : // not defining a row-major ordered contiguous buffer.
    4864             : // Should only be called when at least one of the above conditions are true,
    4865             : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
    4866             : // returning none.
    4867             : // This method will call IRead() again with arrayStep[] == 1,
    4868             : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
    4869             : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
    4870             : // content of that temporary buffer onto pDstBuffer.
    4871           7 : bool GDALMDArray::ReadUsingContiguousIRead(
    4872             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    4873             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
    4874             :     void *pDstBuffer) const
    4875             : {
    4876           7 :     const size_t nDims(GetDimensionCount());
    4877          14 :     std::vector<GUInt64> anTmpStartIdx(nDims);
    4878          14 :     std::vector<size_t> anTmpCount(nDims);
    4879           7 :     const auto &oType = GetDataType();
    4880           7 :     size_t nMemArraySize = oType.GetSize();
    4881          14 :     std::vector<GPtrDiff_t> anTmpStride(nDims);
    4882           7 :     GPtrDiff_t nStride = 1;
    4883          18 :     for (size_t i = nDims; i > 0;)
    4884             :     {
    4885          11 :         --i;
    4886          11 :         if (arrayStep[i] > 0)
    4887           9 :             anTmpStartIdx[i] = arrayStartIdx[i];
    4888             :         else
    4889           2 :             anTmpStartIdx[i] =
    4890           2 :                 arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
    4891             :         const uint64_t nCount =
    4892          11 :             (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
    4893          11 :         if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
    4894             :         {
    4895           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4896             :                      "Read() failed due to too large memory requirement");
    4897           0 :             return false;
    4898             :         }
    4899          11 :         anTmpCount[i] = static_cast<size_t>(nCount);
    4900          11 :         nMemArraySize *= anTmpCount[i];
    4901          11 :         anTmpStride[i] = nStride;
    4902          11 :         nStride *= anTmpCount[i];
    4903             :     }
    4904             :     std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
    4905          14 :         VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
    4906           7 :     if (!pTmpBuffer)
    4907           0 :         return false;
    4908           7 :     if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
    4909          14 :                std::vector<GInt64>(nDims, 1).data(),  // steps
    4910           7 :                anTmpStride.data(), oType, pTmpBuffer.get()))
    4911             :     {
    4912           0 :         return false;
    4913             :     }
    4914          14 :     std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
    4915          18 :     for (size_t i = 0; i < nDims; ++i)
    4916             :     {
    4917          11 :         if (arrayStep[i] > 0)
    4918           9 :             anTmpStartIdx[i] = 0;
    4919             :         else
    4920           2 :             anTmpStartIdx[i] = anTmpCount[i] - 1;
    4921          22 :         apoTmpDims[i] = std::make_shared<GDALDimension>(
    4922          22 :             std::string(), std::string(), std::string(), std::string(),
    4923          22 :             anTmpCount[i]);
    4924             :     }
    4925             :     auto poMEMArray =
    4926          14 :         MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
    4927          21 :     return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
    4928           7 :            poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
    4929           7 :                             bufferStride, bufferDataType, pDstBuffer);
    4930             : }
    4931             : 
    4932             : //! @endcond
    4933             : 
    4934             : /************************************************************************/
    4935             : /*                       GDALSlicedMDArray                              */
    4936             : /************************************************************************/
    4937             : 
    4938             : class GDALSlicedMDArray final : public GDALPamMDArray
    4939             : {
    4940             :   private:
    4941             :     std::shared_ptr<GDALMDArray> m_poParent{};
    4942             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    4943             :     std::vector<size_t> m_mapDimIdxToParentDimIdx{};  // of size m_dims.size()
    4944             :     std::vector<Range>
    4945             :         m_parentRanges{};  // of size m_poParent->GetDimensionCount()
    4946             : 
    4947             :     mutable std::vector<GUInt64> m_parentStart;
    4948             :     mutable std::vector<size_t> m_parentCount;
    4949             :     mutable std::vector<GInt64> m_parentStep;
    4950             :     mutable std::vector<GPtrDiff_t> m_parentStride;
    4951             : 
    4952             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
    4953             :                              const GInt64 *arrayStep,
    4954             :                              const GPtrDiff_t *bufferStride) const;
    4955             : 
    4956             :   protected:
    4957         574 :     explicit GDALSlicedMDArray(
    4958             :         const std::shared_ptr<GDALMDArray> &poParent,
    4959             :         const std::string &viewExpr,
    4960             :         std::vector<std::shared_ptr<GDALDimension>> &&dims,
    4961             :         std::vector<size_t> &&mapDimIdxToParentDimIdx,
    4962             :         std::vector<Range> &&parentRanges)
    4963        1722 :         : GDALAbstractMDArray(std::string(), "Sliced view of " +
    4964        1722 :                                                  poParent->GetFullName() +
    4965        1148 :                                                  " (" + viewExpr + ")"),
    4966        1148 :           GDALPamMDArray(std::string(),
    4967        1148 :                          "Sliced view of " + poParent->GetFullName() + " (" +
    4968        1148 :                              viewExpr + ")",
    4969        1148 :                          GDALPamMultiDim::GetPAM(poParent),
    4970             :                          poParent->GetContext()),
    4971        1148 :           m_poParent(std::move(poParent)), m_dims(std::move(dims)),
    4972         574 :           m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
    4973         574 :           m_parentRanges(std::move(parentRanges)),
    4974         574 :           m_parentStart(m_poParent->GetDimensionCount()),
    4975         574 :           m_parentCount(m_poParent->GetDimensionCount(), 1),
    4976         574 :           m_parentStep(m_poParent->GetDimensionCount()),
    4977        4592 :           m_parentStride(m_poParent->GetDimensionCount())
    4978             :     {
    4979         574 :     }
    4980             : 
    4981             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    4982             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    4983             :                const GDALExtendedDataType &bufferDataType,
    4984             :                void *pDstBuffer) const override;
    4985             : 
    4986             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    4987             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    4988             :                 const GDALExtendedDataType &bufferDataType,
    4989             :                 const void *pSrcBuffer) override;
    4990             : 
    4991             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    4992             :                      CSLConstList papszOptions) const override;
    4993             : 
    4994             :   public:
    4995             :     static std::shared_ptr<GDALSlicedMDArray>
    4996         574 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    4997             :            const std::string &viewExpr,
    4998             :            std::vector<std::shared_ptr<GDALDimension>> &&dims,
    4999             :            std::vector<size_t> &&mapDimIdxToParentDimIdx,
    5000             :            std::vector<Range> &&parentRanges)
    5001             :     {
    5002         574 :         CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
    5003         574 :         CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
    5004             : 
    5005             :         auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
    5006         574 :             poParent, viewExpr, std::move(dims),
    5007         574 :             std::move(mapDimIdxToParentDimIdx), std::move(parentRanges))));
    5008         574 :         newAr->SetSelf(newAr);
    5009         574 :         return newAr;
    5010             :     }
    5011             : 
    5012          55 :     bool IsWritable() const override
    5013             :     {
    5014          55 :         return m_poParent->IsWritable();
    5015             :     }
    5016             : 
    5017         983 :     const std::string &GetFilename() const override
    5018             :     {
    5019         983 :         return m_poParent->GetFilename();
    5020             :     }
    5021             : 
    5022             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5023        3646 :     GetDimensions() const override
    5024             :     {
    5025        3646 :         return m_dims;
    5026             :     }
    5027             : 
    5028        1383 :     const GDALExtendedDataType &GetDataType() const override
    5029             :     {
    5030        1383 :         return m_poParent->GetDataType();
    5031             :     }
    5032             : 
    5033           2 :     const std::string &GetUnit() const override
    5034             :     {
    5035           2 :         return m_poParent->GetUnit();
    5036             :     }
    5037             : 
    5038             :     // bool SetUnit(const std::string& osUnit) override  { return
    5039             :     // m_poParent->SetUnit(osUnit); }
    5040             : 
    5041           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5042             :     {
    5043           4 :         auto poSrcSRS = m_poParent->GetSpatialRef();
    5044           2 :         if (!poSrcSRS)
    5045           1 :             return nullptr;
    5046           2 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
    5047           2 :         std::vector<int> dstMapping;
    5048           3 :         for (int srcAxis : srcMapping)
    5049             :         {
    5050           2 :             bool bFound = false;
    5051           3 :             for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
    5052             :             {
    5053           3 :                 if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
    5054           3 :                     srcAxis - 1)
    5055             :                 {
    5056           2 :                     dstMapping.push_back(static_cast<int>(i) + 1);
    5057           2 :                     bFound = true;
    5058           2 :                     break;
    5059             :                 }
    5060             :             }
    5061           2 :             if (!bFound)
    5062             :             {
    5063           0 :                 dstMapping.push_back(0);
    5064             :             }
    5065             :         }
    5066           2 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
    5067           1 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
    5068           1 :         return poClone;
    5069             :     }
    5070             : 
    5071          55 :     const void *GetRawNoDataValue() const override
    5072             :     {
    5073          55 :         return m_poParent->GetRawNoDataValue();
    5074             :     }
    5075             : 
    5076             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
    5077             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
    5078             : 
    5079           2 :     double GetOffset(bool *pbHasOffset,
    5080             :                      GDALDataType *peStorageType) const override
    5081             :     {
    5082           2 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5083             :     }
    5084             : 
    5085           2 :     double GetScale(bool *pbHasScale,
    5086             :                     GDALDataType *peStorageType) const override
    5087             :     {
    5088           2 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5089             :     }
    5090             : 
    5091             :     // bool SetOffset(double dfOffset) override { return
    5092             :     // m_poParent->SetOffset(dfOffset); }
    5093             : 
    5094             :     // bool SetScale(double dfScale) override { return
    5095             :     // m_poParent->SetScale(dfScale); }
    5096             : 
    5097         197 :     std::vector<GUInt64> GetBlockSize() const override
    5098             :     {
    5099         197 :         std::vector<GUInt64> ret(GetDimensionCount());
    5100         394 :         const auto parentBlockSize(m_poParent->GetBlockSize());
    5101         595 :         for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
    5102             :         {
    5103         398 :             const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
    5104         398 :             if (iOldAxis != static_cast<size_t>(-1))
    5105             :             {
    5106         398 :                 ret[i] = parentBlockSize[iOldAxis];
    5107             :             }
    5108             :         }
    5109         394 :         return ret;
    5110             :     }
    5111             : 
    5112             :     std::shared_ptr<GDALAttribute>
    5113           6 :     GetAttribute(const std::string &osName) const override
    5114             :     {
    5115           6 :         return m_poParent->GetAttribute(osName);
    5116             :     }
    5117             : 
    5118             :     std::vector<std::shared_ptr<GDALAttribute>>
    5119          24 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    5120             :     {
    5121          24 :         return m_poParent->GetAttributes(papszOptions);
    5122             :     }
    5123             : };
    5124             : 
    5125             : /************************************************************************/
    5126             : /*                        PrepareParentArrays()                         */
    5127             : /************************************************************************/
    5128             : 
    5129         475 : void GDALSlicedMDArray::PrepareParentArrays(
    5130             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    5131             :     const GPtrDiff_t *bufferStride) const
    5132             : {
    5133         475 :     const size_t nParentDimCount = m_parentRanges.size();
    5134        1481 :     for (size_t i = 0; i < nParentDimCount; i++)
    5135             :     {
    5136             :         // For dimensions in parent that have no existence in sliced array
    5137        1006 :         m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
    5138             :     }
    5139             : 
    5140        1250 :     for (size_t i = 0; i < m_dims.size(); i++)
    5141             :     {
    5142         775 :         const auto iParent = m_mapDimIdxToParentDimIdx[i];
    5143         775 :         if (iParent != static_cast<size_t>(-1))
    5144             :         {
    5145         773 :             m_parentStart[iParent] =
    5146         773 :                 m_parentRanges[iParent].m_nIncr >= 0
    5147         773 :                     ? m_parentRanges[iParent].m_nStartIdx +
    5148         744 :                           arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
    5149          29 :                     : m_parentRanges[iParent].m_nStartIdx -
    5150          58 :                           arrayStartIdx[i] *
    5151          29 :                               static_cast<GUInt64>(
    5152          29 :                                   -m_parentRanges[iParent].m_nIncr);
    5153         773 :             m_parentCount[iParent] = count[i];
    5154         773 :             if (arrayStep)
    5155             :             {
    5156         772 :                 m_parentStep[iParent] =
    5157         772 :                     count[i] == 1 ? 1 :
    5158             :                                   // other checks should have ensured this does
    5159             :                         // not overflow
    5160         586 :                         arrayStep[i] * m_parentRanges[iParent].m_nIncr;
    5161             :             }
    5162         773 :             if (bufferStride)
    5163             :             {
    5164         772 :                 m_parentStride[iParent] = bufferStride[i];
    5165             :             }
    5166             :         }
    5167             :     }
    5168         475 : }
    5169             : 
    5170             : /************************************************************************/
    5171             : /*                             IRead()                                  */
    5172             : /************************************************************************/
    5173             : 
    5174         442 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5175             :                               const GInt64 *arrayStep,
    5176             :                               const GPtrDiff_t *bufferStride,
    5177             :                               const GDALExtendedDataType &bufferDataType,
    5178             :                               void *pDstBuffer) const
    5179             : {
    5180         442 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5181         884 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
    5182         442 :                             m_parentStep.data(), m_parentStride.data(),
    5183         442 :                             bufferDataType, pDstBuffer);
    5184             : }
    5185             : 
    5186             : /************************************************************************/
    5187             : /*                             IWrite()                                  */
    5188             : /************************************************************************/
    5189             : 
    5190          32 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
    5191             :                                const size_t *count, const GInt64 *arrayStep,
    5192             :                                const GPtrDiff_t *bufferStride,
    5193             :                                const GDALExtendedDataType &bufferDataType,
    5194             :                                const void *pSrcBuffer)
    5195             : {
    5196          32 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5197          64 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
    5198          32 :                              m_parentStep.data(), m_parentStride.data(),
    5199          32 :                              bufferDataType, pSrcBuffer);
    5200             : }
    5201             : 
    5202             : /************************************************************************/
    5203             : /*                             IAdviseRead()                            */
    5204             : /************************************************************************/
    5205             : 
    5206           1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
    5207             :                                     const size_t *count,
    5208             :                                     CSLConstList papszOptions) const
    5209             : {
    5210           1 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
    5211           1 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
    5212           1 :                                   papszOptions);
    5213             : }
    5214             : 
    5215             : /************************************************************************/
    5216             : /*                        CreateSlicedArray()                           */
    5217             : /************************************************************************/
    5218             : 
    5219             : static std::shared_ptr<GDALMDArray>
    5220         592 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
    5221             :                   const std::string &viewExpr, const std::string &activeSlice,
    5222             :                   bool bRenameDimensions,
    5223             :                   std::vector<GDALMDArray::ViewSpec> &viewSpecs)
    5224             : {
    5225         592 :     const auto &srcDims(self->GetDimensions());
    5226         592 :     if (srcDims.empty())
    5227             :     {
    5228           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
    5229           2 :         return nullptr;
    5230             :     }
    5231             : 
    5232        1180 :     CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
    5233         590 :     const auto nTokens = static_cast<size_t>(aosTokens.size());
    5234             : 
    5235        1180 :     std::vector<std::shared_ptr<GDALDimension>> newDims;
    5236        1180 :     std::vector<size_t> mapDimIdxToParentDimIdx;
    5237        1180 :     std::vector<GDALSlicedMDArray::Range> parentRanges;
    5238         590 :     newDims.reserve(nTokens);
    5239         590 :     mapDimIdxToParentDimIdx.reserve(nTokens);
    5240         590 :     parentRanges.reserve(nTokens);
    5241             : 
    5242         590 :     bool bGotEllipsis = false;
    5243         590 :     size_t nCurSrcDim = 0;
    5244        1744 :     for (size_t i = 0; i < nTokens; i++)
    5245             :     {
    5246        1170 :         const char *pszIdxSpec = aosTokens[i];
    5247        1170 :         if (EQUAL(pszIdxSpec, "..."))
    5248             :         {
    5249          37 :             if (bGotEllipsis)
    5250             :             {
    5251           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5252             :                          "Only one single ellipsis is supported");
    5253           2 :                 return nullptr;
    5254             :             }
    5255          35 :             bGotEllipsis = true;
    5256          35 :             const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
    5257          77 :             for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
    5258             :             {
    5259          42 :                 parentRanges.emplace_back(0, 1);
    5260          42 :                 newDims.push_back(srcDims[nCurSrcDim]);
    5261          42 :                 mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5262             :             }
    5263          35 :             continue;
    5264             :         }
    5265        1133 :         else if (EQUAL(pszIdxSpec, "newaxis") ||
    5266        1130 :                  EQUAL(pszIdxSpec, "np.newaxis"))
    5267             :         {
    5268           3 :             newDims.push_back(std::make_shared<GDALDimension>(
    5269           6 :                 std::string(), "newaxis", std::string(), std::string(), 1));
    5270           3 :             mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
    5271           3 :             continue;
    5272             :         }
    5273        1130 :         else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
    5274             :         {
    5275         323 :             if (nCurSrcDim >= srcDims.size())
    5276             :             {
    5277           2 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
    5278             :                          activeSlice.c_str());
    5279           7 :                 return nullptr;
    5280             :             }
    5281             : 
    5282         321 :             auto nVal = CPLAtoGIntBig(pszIdxSpec);
    5283         321 :             GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
    5284         321 :             if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
    5285         317 :                 (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
    5286             :             {
    5287           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5288             :                          "Index " CPL_FRMT_GIB " is out of bounds", nVal);
    5289           5 :                 return nullptr;
    5290             :             }
    5291         316 :             if (nVal < 0)
    5292           0 :                 nVal += nDimSize;
    5293         316 :             parentRanges.emplace_back(nVal, 0);
    5294             :         }
    5295             :         else
    5296             :         {
    5297         807 :             if (nCurSrcDim >= srcDims.size())
    5298             :             {
    5299           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
    5300             :                          activeSlice.c_str());
    5301           7 :                 return nullptr;
    5302             :             }
    5303             : 
    5304             :             CPLStringList aosRangeTokens(
    5305         806 :                 CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
    5306         806 :             int nRangeTokens = aosRangeTokens.size();
    5307         806 :             if (nRangeTokens > 3)
    5308             :             {
    5309           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
    5310             :                          pszIdxSpec);
    5311           1 :                 return nullptr;
    5312             :             }
    5313         805 :             if (nRangeTokens <= 1)
    5314             :             {
    5315           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
    5316             :                          pszIdxSpec);
    5317           1 :                 return nullptr;
    5318             :             }
    5319         804 :             const char *pszStart = aosRangeTokens[0];
    5320         804 :             const char *pszEnd = aosRangeTokens[1];
    5321         804 :             const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
    5322         804 :             GDALSlicedMDArray::Range range;
    5323         804 :             const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
    5324         804 :             range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
    5325        1607 :             if (range.m_nIncr == 0 ||
    5326         803 :                 range.m_nIncr == std::numeric_limits<GInt64>::min())
    5327             :             {
    5328           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
    5329           1 :                 return nullptr;
    5330             :             }
    5331         803 :             auto startIdx(CPLAtoGIntBig(pszStart));
    5332         803 :             if (startIdx < 0)
    5333             :             {
    5334           0 :                 if (nDimSize < static_cast<GUInt64>(-startIdx))
    5335           0 :                     startIdx = 0;
    5336             :                 else
    5337           0 :                     startIdx = nDimSize + startIdx;
    5338             :             }
    5339         803 :             const bool bPosIncr = range.m_nIncr > 0;
    5340         803 :             range.m_nStartIdx = startIdx;
    5341        1606 :             range.m_nStartIdx = EQUAL(pszStart, "")
    5342         803 :                                     ? (bPosIncr ? 0 : nDimSize - 1)
    5343             :                                     : range.m_nStartIdx;
    5344         803 :             if (range.m_nStartIdx >= nDimSize - 1)
    5345         185 :                 range.m_nStartIdx = nDimSize - 1;
    5346         803 :             auto endIdx(CPLAtoGIntBig(pszEnd));
    5347         803 :             if (endIdx < 0)
    5348             :             {
    5349           1 :                 const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
    5350           1 :                 if (nDimSize < positiveEndIdx)
    5351           0 :                     endIdx = 0;
    5352             :                 else
    5353           1 :                     endIdx = nDimSize - positiveEndIdx;
    5354             :             }
    5355         803 :             GUInt64 nEndIdx = endIdx;
    5356         803 :             nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
    5357         803 :             if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
    5358         801 :                 (!bPosIncr && range.m_nStartIdx <= nEndIdx))
    5359             :             {
    5360           3 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5361             :                          "Output dimension of size 0 is not allowed");
    5362           3 :                 return nullptr;
    5363             :             }
    5364         800 :             int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
    5365         800 :             const auto nAbsIncr = std::abs(range.m_nIncr);
    5366         800 :             const GUInt64 newSize =
    5367             :                 bPosIncr
    5368         833 :                     ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
    5369          33 :                     : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
    5370        1322 :             if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
    5371         522 :                 newSize == srcDims[nCurSrcDim]->GetSize())
    5372             :             {
    5373         153 :                 newDims.push_back(srcDims[nCurSrcDim]);
    5374             :             }
    5375             :             else
    5376             :             {
    5377         647 :                 std::string osNewDimName(srcDims[nCurSrcDim]->GetName());
    5378         647 :                 if (bRenameDimensions)
    5379             :                 {
    5380             :                     osNewDimName =
    5381        1210 :                         "subset_" + srcDims[nCurSrcDim]->GetName() +
    5382             :                         CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
    5383             :                                    "_" CPL_FRMT_GUIB,
    5384         605 :                                    static_cast<GUIntBig>(range.m_nStartIdx),
    5385         605 :                                    static_cast<GIntBig>(range.m_nIncr),
    5386         605 :                                    static_cast<GUIntBig>(newSize));
    5387             :                 }
    5388         647 :                 newDims.push_back(std::make_shared<GDALDimension>(
    5389        1294 :                     std::string(), osNewDimName, srcDims[nCurSrcDim]->GetType(),
    5390        1294 :                     range.m_nIncr > 0 ? srcDims[nCurSrcDim]->GetDirection()
    5391             :                                       : std::string(),
    5392             :                     newSize));
    5393             :             }
    5394         800 :             mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5395         800 :             parentRanges.emplace_back(range);
    5396             :         }
    5397             : 
    5398        1116 :         nCurSrcDim++;
    5399             :     }
    5400         647 :     for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
    5401             :     {
    5402          73 :         parentRanges.emplace_back(0, 1);
    5403          73 :         newDims.push_back(srcDims[nCurSrcDim]);
    5404          73 :         mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5405             :     }
    5406             : 
    5407         574 :     GDALMDArray::ViewSpec viewSpec;
    5408         574 :     viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
    5409         574 :     viewSpec.m_parentRanges = parentRanges;
    5410         574 :     viewSpecs.emplace_back(std::move(viewSpec));
    5411             : 
    5412        1148 :     return GDALSlicedMDArray::Create(self, viewExpr, std::move(newDims),
    5413         574 :                                      std::move(mapDimIdxToParentDimIdx),
    5414        1148 :                                      std::move(parentRanges));
    5415             : }
    5416             : 
    5417             : /************************************************************************/
    5418             : /*                       GDALExtractFieldMDArray                        */
    5419             : /************************************************************************/
    5420             : 
    5421             : class GDALExtractFieldMDArray final : public GDALPamMDArray
    5422             : {
    5423             :   private:
    5424             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5425             :     GDALExtendedDataType m_dt;
    5426             :     std::string m_srcCompName;
    5427             :     mutable std::vector<GByte> m_pabyNoData{};
    5428             : 
    5429             :   protected:
    5430          62 :     GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
    5431             :                             const std::string &fieldName,
    5432             :                             const std::unique_ptr<GDALEDTComponent> &srcComp)
    5433         248 :         : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
    5434         124 :                                                  " of " +
    5435          62 :                                                  poParent->GetFullName()),
    5436             :           GDALPamMDArray(
    5437         124 :               std::string(),
    5438         124 :               "Extract field " + fieldName + " of " + poParent->GetFullName(),
    5439         124 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
    5440             :           m_poParent(poParent), m_dt(srcComp->GetType()),
    5441         310 :           m_srcCompName(srcComp->GetName())
    5442             :     {
    5443          62 :         m_pabyNoData.resize(m_dt.GetSize());
    5444          62 :     }
    5445             : 
    5446             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5447             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5448             :                const GDALExtendedDataType &bufferDataType,
    5449             :                void *pDstBuffer) const override;
    5450             : 
    5451           1 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5452             :                      CSLConstList papszOptions) const override
    5453             :     {
    5454           1 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
    5455             :     }
    5456             : 
    5457             :   public:
    5458             :     static std::shared_ptr<GDALExtractFieldMDArray>
    5459          62 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5460             :            const std::string &fieldName,
    5461             :            const std::unique_ptr<GDALEDTComponent> &srcComp)
    5462             :     {
    5463             :         auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
    5464          62 :             new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
    5465          62 :         newAr->SetSelf(newAr);
    5466          62 :         return newAr;
    5467             :     }
    5468             : 
    5469         124 :     ~GDALExtractFieldMDArray()
    5470          62 :     {
    5471          62 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
    5472         124 :     }
    5473             : 
    5474          40 :     bool IsWritable() const override
    5475             :     {
    5476          40 :         return m_poParent->IsWritable();
    5477             :     }
    5478             : 
    5479         204 :     const std::string &GetFilename() const override
    5480             :     {
    5481         204 :         return m_poParent->GetFilename();
    5482             :     }
    5483             : 
    5484             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5485         300 :     GetDimensions() const override
    5486             :     {
    5487         300 :         return m_poParent->GetDimensions();
    5488             :     }
    5489             : 
    5490         246 :     const GDALExtendedDataType &GetDataType() const override
    5491             :     {
    5492         246 :         return m_dt;
    5493             :     }
    5494             : 
    5495           2 :     const std::string &GetUnit() const override
    5496             :     {
    5497           2 :         return m_poParent->GetUnit();
    5498             :     }
    5499             : 
    5500           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5501             :     {
    5502           2 :         return m_poParent->GetSpatialRef();
    5503             :     }
    5504             : 
    5505          56 :     const void *GetRawNoDataValue() const override
    5506             :     {
    5507          56 :         const void *parentNoData = m_poParent->GetRawNoDataValue();
    5508          56 :         if (parentNoData == nullptr)
    5509           1 :             return nullptr;
    5510             : 
    5511          55 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
    5512          55 :         memset(&m_pabyNoData[0], 0, m_dt.GetSize());
    5513             : 
    5514         110 :         std::vector<std::unique_ptr<GDALEDTComponent>> comps;
    5515         110 :         comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
    5516         110 :             new GDALEDTComponent(m_srcCompName, 0, m_dt)));
    5517          55 :         auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
    5518         165 :                                                 std::move(comps)));
    5519             : 
    5520          55 :         GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
    5521          55 :                                         &m_pabyNoData[0], tmpDT);
    5522             : 
    5523          55 :         return &m_pabyNoData[0];
    5524             :     }
    5525             : 
    5526           2 :     double GetOffset(bool *pbHasOffset,
    5527             :                      GDALDataType *peStorageType) const override
    5528             :     {
    5529           2 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5530             :     }
    5531             : 
    5532           2 :     double GetScale(bool *pbHasScale,
    5533             :                     GDALDataType *peStorageType) const override
    5534             :     {
    5535           2 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5536             :     }
    5537             : 
    5538          41 :     std::vector<GUInt64> GetBlockSize() const override
    5539             :     {
    5540          41 :         return m_poParent->GetBlockSize();
    5541             :     }
    5542             : };
    5543             : 
    5544             : /************************************************************************/
    5545             : /*                             IRead()                                  */
    5546             : /************************************************************************/
    5547             : 
    5548          46 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
    5549             :                                     const size_t *count,
    5550             :                                     const GInt64 *arrayStep,
    5551             :                                     const GPtrDiff_t *bufferStride,
    5552             :                                     const GDALExtendedDataType &bufferDataType,
    5553             :                                     void *pDstBuffer) const
    5554             : {
    5555          92 :     std::vector<std::unique_ptr<GDALEDTComponent>> comps;
    5556          92 :     comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
    5557          92 :         new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
    5558             :     auto tmpDT(GDALExtendedDataType::Create(
    5559          92 :         std::string(), bufferDataType.GetSize(), std::move(comps)));
    5560             : 
    5561          46 :     return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
    5562          92 :                             tmpDT, pDstBuffer);
    5563             : }
    5564             : 
    5565             : /************************************************************************/
    5566             : /*                      CreateFieldNameExtractArray()                   */
    5567             : /************************************************************************/
    5568             : 
    5569             : static std::shared_ptr<GDALMDArray>
    5570          63 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
    5571             :                             const std::string &fieldName)
    5572             : {
    5573          63 :     CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
    5574          63 :     const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
    5575         125 :     for (const auto &comp : self->GetDataType().GetComponents())
    5576             :     {
    5577         124 :         if (comp->GetName() == fieldName)
    5578             :         {
    5579          62 :             srcComp = &comp;
    5580          62 :             break;
    5581             :         }
    5582             :     }
    5583          63 :     if (srcComp == nullptr)
    5584             :     {
    5585           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
    5586             :                  fieldName.c_str());
    5587           1 :         return nullptr;
    5588             :     }
    5589          62 :     return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
    5590             : }
    5591             : 
    5592             : /************************************************************************/
    5593             : /*                             GetView()                                */
    5594             : /************************************************************************/
    5595             : 
    5596             : // clang-format off
    5597             : /** Return a view of the array using slicing or field access.
    5598             :  *
    5599             :  * The slice expression uses the same syntax as NumPy basic slicing and
    5600             :  * indexing. See
    5601             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
    5602             :  * Or it can use field access by name. See
    5603             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
    5604             :  *
    5605             :  * Multiple [] bracket elements can be concatenated, with a slice expression
    5606             :  * or field name inside each.
    5607             :  *
    5608             :  * For basic slicing and indexing, inside each [] bracket element, a list of
    5609             :  * indexes that apply to successive source dimensions, can be specified, using
    5610             :  * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
    5611             :  * or newaxis, using a comma separator.
    5612             :  *
    5613             :  * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
    5614             :  * <ul>
    5615             :  * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
    5616             :  *     at index 1 in the first dimension, and index 2 in the second dimension
    5617             :  *     from the source array. That is 5</li>
    5618             :  * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
    5619             :  * implemented internally doing this intermediate slicing approach.</li>
    5620             :  * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
    5621             :  * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
    5622             :  *     first dimension. That is [4,5,6,7].</li>
    5623             :  * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
    5624             :  *     second dimension. That is [2,6].</li>
    5625             :  * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
    5626             :  * the second dimension. That is [[2],[6]].</li>
    5627             :  * <li>GetView("[::,2]"): Same as
    5628             :  * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
    5629             :  * ellipsis only expands to one dimension here.</li>
    5630             :  * <li>GetView("[:,::2]"):
    5631             :  * returns a 2-dimensional array, with even-indexed elements of the second
    5632             :  * dimension. That is [[0,2],[4,6]].</li>
    5633             :  * <li>GetView("[:,1::2]"): returns a
    5634             :  * 2-dimensional array, with odd-indexed elements of the second dimension. That
    5635             :  * is [[1,3],[5,7]].</li>
    5636             :  * <li>GetView("[:,1:3:]"): returns a 2-dimensional
    5637             :  * array, with elements of the second dimension with index in the range [1,3[.
    5638             :  * That is [[1,2],[5,6]].</li>
    5639             :  * <li>GetView("[::-1,:]"): returns a 2-dimensional
    5640             :  * array, with the values in first dimension reversed. That is
    5641             :  * [[4,5,6,7],[0,1,2,3]].</li>
    5642             :  * <li>GetView("[newaxis,...]"): returns a
    5643             :  * 3-dimensional array, with an additional dimension of size 1 put at the
    5644             :  * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
    5645             :  * </ul>
    5646             :  *
    5647             :  * One difference with NumPy behavior is that ranges that would result in
    5648             :  * zero elements are not allowed (dimensions of size 0 not being allowed in the
    5649             :  * GDAL multidimensional model).
    5650             :  *
    5651             :  * For field access, the syntax to use is ["field_name"] or ['field_name'].
    5652             :  * Multiple field specification is not supported currently.
    5653             :  *
    5654             :  * Both type of access can be combined, e.g. GetView("[1]['field_name']")
    5655             :  *
    5656             :  * \note When using the GDAL Python bindings, natural Python syntax can be
    5657             :  * used. That is ar[0,::,1]["foo"] will be internally translated to
    5658             :  * ar.GetView("[0,::,1]['foo']")
    5659             :  * \note When using the C++ API and integer indexing only, you may use the
    5660             :  * at(idx0, idx1, ...) method.
    5661             :  *
    5662             :  * The returned array holds a reference to the original one, and thus is
    5663             :  * a view of it (not a copy). If the content of the original array changes,
    5664             :  * the content of the view array too. When using basic slicing and indexing,
    5665             :  * the view can be written if the underlying array is writable.
    5666             :  *
    5667             :  * This is the same as the C function GDALMDArrayGetView()
    5668             :  *
    5669             :  * @param viewExpr Expression expressing basic slicing and indexing, or field
    5670             :  * access.
    5671             :  * @return a new array, that holds a reference to the original one, and thus is
    5672             :  * a view of it (not a copy), or nullptr in case of error.
    5673             :  */
    5674             : // clang-format on
    5675             : 
    5676             : std::shared_ptr<GDALMDArray>
    5677         598 : GDALMDArray::GetView(const std::string &viewExpr) const
    5678             : {
    5679        1196 :     std::vector<ViewSpec> viewSpecs;
    5680        1196 :     return GetView(viewExpr, true, viewSpecs);
    5681             : }
    5682             : 
    5683             : //! @cond Doxygen_Suppress
    5684             : std::shared_ptr<GDALMDArray>
    5685         661 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
    5686             :                      std::vector<ViewSpec> &viewSpecs) const
    5687             : {
    5688        1322 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    5689         661 :     if (!self)
    5690             :     {
    5691           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    5692             :                  "Driver implementation issue: m_pSelf not set !");
    5693           1 :         return nullptr;
    5694             :     }
    5695         660 :     std::string curExpr(viewExpr);
    5696             :     while (true)
    5697             :     {
    5698         663 :         if (curExpr.empty() || curExpr[0] != '[')
    5699             :         {
    5700           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    5701             :                      "Slice string should start with ['");
    5702         660 :             return nullptr;
    5703             :         }
    5704             : 
    5705         661 :         std::string fieldName;
    5706             :         size_t endExpr;
    5707         661 :         if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
    5708             :         {
    5709          67 :             if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
    5710             :             {
    5711           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5712             :                          "Field access not allowed on non-compound data type");
    5713           2 :                 return nullptr;
    5714             :             }
    5715          65 :             size_t idx = 2;
    5716         572 :             for (; idx < curExpr.size(); idx++)
    5717             :             {
    5718         571 :                 const char ch = curExpr[idx];
    5719         571 :                 if (ch == curExpr[1])
    5720          64 :                     break;
    5721         507 :                 if (ch == '\\' && idx + 1 < curExpr.size())
    5722             :                 {
    5723           1 :                     fieldName += curExpr[idx + 1];
    5724           1 :                     idx++;
    5725             :                 }
    5726             :                 else
    5727             :                 {
    5728         506 :                     fieldName += ch;
    5729             :                 }
    5730             :             }
    5731          65 :             if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
    5732             :             {
    5733           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5734             :                          "Invalid field access specification");
    5735           2 :                 return nullptr;
    5736             :             }
    5737          63 :             endExpr = idx + 1;
    5738             :         }
    5739             :         else
    5740             :         {
    5741         594 :             endExpr = curExpr.find(']');
    5742             :         }
    5743         657 :         if (endExpr == std::string::npos)
    5744             :         {
    5745           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
    5746           1 :             return nullptr;
    5747             :         }
    5748         656 :         if (endExpr == 1)
    5749             :         {
    5750           1 :             CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
    5751           1 :             return nullptr;
    5752             :         }
    5753         655 :         std::string activeSlice(curExpr.substr(1, endExpr - 1));
    5754             : 
    5755         655 :         if (!fieldName.empty())
    5756             :         {
    5757         126 :             ViewSpec viewSpec;
    5758          63 :             viewSpec.m_osFieldName = fieldName;
    5759          63 :             viewSpecs.emplace_back(std::move(viewSpec));
    5760             :         }
    5761             : 
    5762         655 :         auto newArray = !fieldName.empty()
    5763             :                             ? CreateFieldNameExtractArray(self, fieldName)
    5764             :                             : CreateSlicedArray(self, viewExpr, activeSlice,
    5765         655 :                                                 bRenameDimensions, viewSpecs);
    5766             : 
    5767         655 :         if (endExpr == curExpr.size() - 1)
    5768             :         {
    5769         652 :             return newArray;
    5770             :         }
    5771           3 :         self = std::move(newArray);
    5772           3 :         curExpr = curExpr.substr(endExpr + 1);
    5773           3 :     }
    5774             : }
    5775             : 
    5776             : //! @endcond
    5777             : 
    5778             : std::shared_ptr<GDALMDArray>
    5779          19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
    5780             : {
    5781          19 :     std::string osExpr("[");
    5782          19 :     bool bFirst = true;
    5783          45 :     for (const auto &idx : indices)
    5784             :     {
    5785          26 :         if (!bFirst)
    5786           7 :             osExpr += ',';
    5787          26 :         bFirst = false;
    5788          26 :         osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
    5789             :     }
    5790          57 :     return GetView(osExpr + ']');
    5791             : }
    5792             : 
    5793             : /************************************************************************/
    5794             : /*                            operator[]                                */
    5795             : /************************************************************************/
    5796             : 
    5797             : /** Return a view of the array using field access
    5798             :  *
    5799             :  * Equivalent of GetView("['fieldName']")
    5800             :  *
    5801             :  * \note When operationg on a shared_ptr, use (*array)["fieldName"] syntax.
    5802             :  */
    5803             : std::shared_ptr<GDALMDArray>
    5804           2 : GDALMDArray::operator[](const std::string &fieldName) const
    5805             : {
    5806           2 :     return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
    5807           4 :                                             .replaceAll('\\', "\\\\")
    5808           4 :                                             .replaceAll('\'', "\\\'")
    5809           6 :                                             .c_str()));
    5810             : }
    5811             : 
    5812             : /************************************************************************/
    5813             : /*                      GDALMDArrayTransposed                           */
    5814             : /************************************************************************/
    5815             : 
    5816             : class GDALMDArrayTransposed final : public GDALPamMDArray
    5817             : {
    5818             :   private:
    5819             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5820             :     std::vector<int> m_anMapNewAxisToOldAxis{};
    5821             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    5822             : 
    5823             :     mutable std::vector<GUInt64> m_parentStart;
    5824             :     mutable std::vector<size_t> m_parentCount;
    5825             :     mutable std::vector<GInt64> m_parentStep;
    5826             :     mutable std::vector<GPtrDiff_t> m_parentStride;
    5827             : 
    5828             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
    5829             :                              const GInt64 *arrayStep,
    5830             :                              const GPtrDiff_t *bufferStride) const;
    5831             : 
    5832             :     static std::string
    5833          84 :     MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
    5834             :     {
    5835          84 :         std::string ret;
    5836          84 :         ret += '[';
    5837         312 :         for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
    5838             :         {
    5839         228 :             if (i > 0)
    5840         144 :                 ret += ',';
    5841         228 :             ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
    5842             :         }
    5843          84 :         ret += ']';
    5844          84 :         return ret;
    5845             :     }
    5846             : 
    5847             :   protected:
    5848          42 :     GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
    5849             :                           const std::vector<int> &anMapNewAxisToOldAxis,
    5850             :                           std::vector<std::shared_ptr<GDALDimension>> &&dims)
    5851          84 :         : GDALAbstractMDArray(std::string(),
    5852          84 :                               "Transposed view of " + poParent->GetFullName() +
    5853          84 :                                   " along " +
    5854          42 :                                   MappingToStr(anMapNewAxisToOldAxis)),
    5855          84 :           GDALPamMDArray(std::string(),
    5856          84 :                          "Transposed view of " + poParent->GetFullName() +
    5857         168 :                              " along " + MappingToStr(anMapNewAxisToOldAxis),
    5858          84 :                          GDALPamMultiDim::GetPAM(poParent),
    5859             :                          poParent->GetContext()),
    5860          42 :           m_poParent(std::move(poParent)),
    5861             :           m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
    5862          42 :           m_dims(std::move(dims)),
    5863          42 :           m_parentStart(m_poParent->GetDimensionCount()),
    5864          42 :           m_parentCount(m_poParent->GetDimensionCount()),
    5865          42 :           m_parentStep(m_poParent->GetDimensionCount()),
    5866         336 :           m_parentStride(m_poParent->GetDimensionCount())
    5867             :     {
    5868          42 :     }
    5869             : 
    5870             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5871             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5872             :                const GDALExtendedDataType &bufferDataType,
    5873             :                void *pDstBuffer) const override;
    5874             : 
    5875             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    5876             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5877             :                 const GDALExtendedDataType &bufferDataType,
    5878             :                 const void *pSrcBuffer) override;
    5879             : 
    5880             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5881             :                      CSLConstList papszOptions) const override;
    5882             : 
    5883             :   public:
    5884             :     static std::shared_ptr<GDALMDArrayTransposed>
    5885          42 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5886             :            const std::vector<int> &anMapNewAxisToOldAxis)
    5887             :     {
    5888          42 :         const auto &parentDims(poParent->GetDimensions());
    5889          84 :         std::vector<std::shared_ptr<GDALDimension>> dims;
    5890         156 :         for (const auto iOldAxis : anMapNewAxisToOldAxis)
    5891             :         {
    5892         114 :             if (iOldAxis < 0)
    5893             :             {
    5894           1 :                 dims.push_back(std::make_shared<GDALDimension>(
    5895           2 :                     std::string(), "newaxis", std::string(), std::string(), 1));
    5896             :             }
    5897             :             else
    5898             :             {
    5899         113 :                 dims.emplace_back(parentDims[iOldAxis]);
    5900             :             }
    5901             :         }
    5902             : 
    5903             :         auto newAr(
    5904             :             std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
    5905          42 :                 poParent, anMapNewAxisToOldAxis, std::move(dims))));
    5906          42 :         newAr->SetSelf(newAr);
    5907          84 :         return newAr;
    5908             :     }
    5909             : 
    5910           1 :     bool IsWritable() const override
    5911             :     {
    5912           1 :         return m_poParent->IsWritable();
    5913             :     }
    5914             : 
    5915          84 :     const std::string &GetFilename() const override
    5916             :     {
    5917          84 :         return m_poParent->GetFilename();
    5918             :     }
    5919             : 
    5920             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5921         358 :     GetDimensions() const override
    5922             :     {
    5923         358 :         return m_dims;
    5924             :     }
    5925             : 
    5926         141 :     const GDALExtendedDataType &GetDataType() const override
    5927             :     {
    5928         141 :         return m_poParent->GetDataType();
    5929             :     }
    5930             : 
    5931           4 :     const std::string &GetUnit() const override
    5932             :     {
    5933           4 :         return m_poParent->GetUnit();
    5934             :     }
    5935             : 
    5936           5 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5937             :     {
    5938          10 :         auto poSrcSRS = m_poParent->GetSpatialRef();
    5939           5 :         if (!poSrcSRS)
    5940           2 :             return nullptr;
    5941           6 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
    5942           6 :         std::vector<int> dstMapping;
    5943           9 :         for (int srcAxis : srcMapping)
    5944             :         {
    5945           6 :             bool bFound = false;
    5946          14 :             for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
    5947             :             {
    5948          14 :                 if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
    5949             :                 {
    5950           6 :                     dstMapping.push_back(static_cast<int>(i) + 1);
    5951           6 :                     bFound = true;
    5952           6 :                     break;
    5953             :                 }
    5954             :             }
    5955           6 :             if (!bFound)
    5956             :             {
    5957           0 :                 dstMapping.push_back(0);
    5958             :             }
    5959             :         }
    5960           6 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
    5961           3 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
    5962           3 :         return poClone;
    5963             :     }
    5964             : 
    5965           4 :     const void *GetRawNoDataValue() const override
    5966             :     {
    5967           4 :         return m_poParent->GetRawNoDataValue();
    5968             :     }
    5969             : 
    5970             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
    5971             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
    5972             : 
    5973           4 :     double GetOffset(bool *pbHasOffset,
    5974             :                      GDALDataType *peStorageType) const override
    5975             :     {
    5976           4 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5977             :     }
    5978             : 
    5979           4 :     double GetScale(bool *pbHasScale,
    5980             :                     GDALDataType *peStorageType) const override
    5981             :     {
    5982           4 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5983             :     }
    5984             : 
    5985             :     // bool SetOffset(double dfOffset) override { return
    5986             :     // m_poParent->SetOffset(dfOffset); }
    5987             : 
    5988             :     // bool SetScale(double dfScale) override { return
    5989             :     // m_poParent->SetScale(dfScale); }
    5990             : 
    5991           3 :     std::vector<GUInt64> GetBlockSize() const override
    5992             :     {
    5993           3 :         std::vector<GUInt64> ret(GetDimensionCount());
    5994           6 :         const auto parentBlockSize(m_poParent->GetBlockSize());
    5995          11 :         for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
    5996             :         {
    5997           8 :             const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
    5998           8 :             if (iOldAxis >= 0)
    5999             :             {
    6000           7 :                 ret[i] = parentBlockSize[iOldAxis];
    6001             :             }
    6002             :         }
    6003           6 :         return ret;
    6004             :     }
    6005             : 
    6006             :     std::shared_ptr<GDALAttribute>
    6007           1 :     GetAttribute(const std::string &osName) const override
    6008             :     {
    6009           1 :         return m_poParent->GetAttribute(osName);
    6010             :     }
    6011             : 
    6012             :     std::vector<std::shared_ptr<GDALAttribute>>
    6013           6 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    6014             :     {
    6015           6 :         return m_poParent->GetAttributes(papszOptions);
    6016             :     }
    6017             : };
    6018             : 
    6019             : /************************************************************************/
    6020             : /*                         PrepareParentArrays()                        */
    6021             : /************************************************************************/
    6022             : 
    6023          47 : void GDALMDArrayTransposed::PrepareParentArrays(
    6024             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    6025             :     const GPtrDiff_t *bufferStride) const
    6026             : {
    6027         176 :     for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
    6028             :     {
    6029         129 :         const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
    6030         129 :         if (iOldAxis >= 0)
    6031             :         {
    6032         128 :             m_parentStart[iOldAxis] = arrayStartIdx[i];
    6033         128 :             m_parentCount[iOldAxis] = count[i];
    6034         128 :             if (arrayStep)  // only null when called from IAdviseRead()
    6035             :             {
    6036         126 :                 m_parentStep[iOldAxis] = arrayStep[i];
    6037             :             }
    6038         128 :             if (bufferStride)  // only null when called from IAdviseRead()
    6039             :             {
    6040         126 :                 m_parentStride[iOldAxis] = bufferStride[i];
    6041             :             }
    6042             :         }
    6043             :     }
    6044          47 : }
    6045             : 
    6046             : /************************************************************************/
    6047             : /*                             IRead()                                  */
    6048             : /************************************************************************/
    6049             : 
    6050          44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
    6051             :                                   const size_t *count, const GInt64 *arrayStep,
    6052             :                                   const GPtrDiff_t *bufferStride,
    6053             :                                   const GDALExtendedDataType &bufferDataType,
    6054             :                                   void *pDstBuffer) const
    6055             : {
    6056          44 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    6057          88 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
    6058          44 :                             m_parentStep.data(), m_parentStride.data(),
    6059          44 :                             bufferDataType, pDstBuffer);
    6060             : }
    6061             : 
    6062             : /************************************************************************/
    6063             : /*                            IWrite()                                  */
    6064             : /************************************************************************/
    6065             : 
    6066           2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
    6067             :                                    const size_t *count, const GInt64 *arrayStep,
    6068             :                                    const GPtrDiff_t *bufferStride,
    6069             :                                    const GDALExtendedDataType &bufferDataType,
    6070             :                                    const void *pSrcBuffer)
    6071             : {
    6072           2 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    6073           4 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
    6074           2 :                              m_parentStep.data(), m_parentStride.data(),
    6075           2 :                              bufferDataType, pSrcBuffer);
    6076             : }
    6077             : 
    6078             : /************************************************************************/
    6079             : /*                             IAdviseRead()                            */
    6080             : /************************************************************************/
    6081             : 
    6082           1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
    6083             :                                         const size_t *count,
    6084             :                                         CSLConstList papszOptions) const
    6085             : {
    6086           1 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
    6087           1 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
    6088           1 :                                   papszOptions);
    6089             : }
    6090             : 
    6091             : /************************************************************************/
    6092             : /*                           Transpose()                                */
    6093             : /************************************************************************/
    6094             : 
    6095             : /** Return a view of the array whose axis have been reordered.
    6096             :  *
    6097             :  * The anMapNewAxisToOldAxis parameter should contain all the values between 0
    6098             :  * and GetDimensionCount() - 1, and each only once.
    6099             :  * -1 can be used as a special index value to ask for the insertion of a new
    6100             :  * axis of size 1.
    6101             :  * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
    6102             :  * index of one of its dimension, it corresponds to the axis of index
    6103             :  * anMapNewAxisToOldAxis[i] from the current array.
    6104             :  *
    6105             :  * This is similar to the numpy.transpose() method
    6106             :  *
    6107             :  * The returned array holds a reference to the original one, and thus is
    6108             :  * a view of it (not a copy). If the content of the original array changes,
    6109             :  * the content of the view array too. The view can be written if the underlying
    6110             :  * array is writable.
    6111             :  *
    6112             :  * Note that I/O performance in such a transposed view might be poor.
    6113             :  *
    6114             :  * This is the same as the C function GDALMDArrayTranspose().
    6115             :  *
    6116             :  * @return a new array, that holds a reference to the original one, and thus is
    6117             :  * a view of it (not a copy), or nullptr in case of error.
    6118             :  */
    6119             : std::shared_ptr<GDALMDArray>
    6120          50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
    6121             : {
    6122         100 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    6123          50 :     if (!self)
    6124             :     {
    6125           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6126             :                  "Driver implementation issue: m_pSelf not set !");
    6127           0 :         return nullptr;
    6128             :     }
    6129          50 :     const int nDims = static_cast<int>(GetDimensionCount());
    6130         100 :     std::vector<bool> alreadyUsedOldAxis(nDims, false);
    6131          50 :     int nCountOldAxis = 0;
    6132         179 :     for (const auto iOldAxis : anMapNewAxisToOldAxis)
    6133             :     {
    6134         133 :         if (iOldAxis < -1 || iOldAxis >= nDims)
    6135             :         {
    6136           3 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
    6137           4 :             return nullptr;
    6138             :         }
    6139         130 :         if (iOldAxis >= 0)
    6140             :         {
    6141         128 :             if (alreadyUsedOldAxis[iOldAxis])
    6142             :             {
    6143           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
    6144             :                          iOldAxis);
    6145           1 :                 return nullptr;
    6146             :             }
    6147         127 :             alreadyUsedOldAxis[iOldAxis] = true;
    6148         127 :             nCountOldAxis++;
    6149             :         }
    6150             :     }
    6151          46 :     if (nCountOldAxis != nDims)
    6152             :     {
    6153           4 :         CPLError(CE_Failure, CPLE_AppDefined,
    6154             :                  "One or several original axis missing");
    6155           4 :         return nullptr;
    6156             :     }
    6157          42 :     return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
    6158             : }
    6159             : 
    6160             : /************************************************************************/
    6161             : /*                             IRead()                                  */
    6162             : /************************************************************************/
    6163             : 
    6164          16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
    6165             :                                 const size_t *count, const GInt64 *arrayStep,
    6166             :                                 const GPtrDiff_t *bufferStride,
    6167             :                                 const GDALExtendedDataType &bufferDataType,
    6168             :                                 void *pDstBuffer) const
    6169             : {
    6170          16 :     const double dfScale = m_dfScale;
    6171          16 :     const double dfOffset = m_dfOffset;
    6172          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
    6173             :     const auto dtDouble =
    6174          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
    6175          16 :     const size_t nDTSize = dtDouble.GetSize();
    6176          16 :     const bool bTempBufferNeeded = (dtDouble != bufferDataType);
    6177             : 
    6178          16 :     double adfSrcNoData[2] = {0, 0};
    6179          16 :     if (m_bHasNoData)
    6180             :     {
    6181           9 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
    6182           9 :                                         m_poParent->GetDataType(),
    6183             :                                         &adfSrcNoData[0], dtDouble);
    6184             :     }
    6185             : 
    6186          16 :     const auto nDims = GetDimensions().size();
    6187          16 :     if (nDims == 0)
    6188             :     {
    6189             :         double adfVal[2];
    6190           9 :         if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
    6191             :                               dtDouble, &adfVal[0]))
    6192             :         {
    6193           0 :             return false;
    6194             :         }
    6195           9 :         if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
    6196             :         {
    6197           6 :             adfVal[0] = adfVal[0] * dfScale + dfOffset;
    6198           6 :             if (bDTIsComplex)
    6199             :             {
    6200           2 :                 adfVal[1] = adfVal[1] * dfScale + dfOffset;
    6201             :             }
    6202           6 :             GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
    6203             :                                             bufferDataType);
    6204             :         }
    6205             :         else
    6206             :         {
    6207           3 :             GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
    6208             :                                             pDstBuffer, bufferDataType);
    6209             :         }
    6210           9 :         return true;
    6211             :     }
    6212             : 
    6213          14 :     std::vector<GPtrDiff_t> actualBufferStrideVector;
    6214           7 :     const GPtrDiff_t *actualBufferStridePtr = bufferStride;
    6215           7 :     void *pTempBuffer = pDstBuffer;
    6216           7 :     if (bTempBufferNeeded)
    6217             :     {
    6218           2 :         size_t nElts = 1;
    6219           2 :         actualBufferStrideVector.resize(nDims);
    6220           7 :         for (size_t i = 0; i < nDims; i++)
    6221           5 :             nElts *= count[i];
    6222           2 :         actualBufferStrideVector.back() = 1;
    6223           5 :         for (size_t i = nDims - 1; i > 0;)
    6224             :         {
    6225           3 :             --i;
    6226           3 :             actualBufferStrideVector[i] =
    6227           3 :                 actualBufferStrideVector[i + 1] * count[i + 1];
    6228             :         }
    6229           2 :         actualBufferStridePtr = actualBufferStrideVector.data();
    6230           2 :         pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
    6231           2 :         if (!pTempBuffer)
    6232           0 :             return false;
    6233             :     }
    6234           7 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
    6235             :                           actualBufferStridePtr, dtDouble, pTempBuffer))
    6236             :     {
    6237           0 :         if (bTempBufferNeeded)
    6238           0 :             VSIFree(pTempBuffer);
    6239           0 :         return false;
    6240             :     }
    6241             : 
    6242             :     struct Stack
    6243             :     {
    6244             :         size_t nIters = 0;
    6245             :         double *src_ptr = nullptr;
    6246             :         GByte *dst_ptr = nullptr;
    6247             :         GPtrDiff_t src_inc_offset = 0;
    6248             :         GPtrDiff_t dst_inc_offset = 0;
    6249             :     };
    6250             : 
    6251           7 :     std::vector<Stack> stack(nDims);
    6252           7 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    6253          23 :     for (size_t i = 0; i < nDims; i++)
    6254             :     {
    6255          32 :         stack[i].src_inc_offset =
    6256          16 :             actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
    6257          16 :         stack[i].dst_inc_offset =
    6258          16 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6259             :     }
    6260           7 :     stack[0].src_ptr = static_cast<double *>(pTempBuffer);
    6261           7 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    6262             : 
    6263           7 :     size_t dimIdx = 0;
    6264           7 :     const size_t nDimsMinus1 = nDims - 1;
    6265             :     GByte abyDstNoData[16];
    6266           7 :     CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
    6267           7 :     GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
    6268             :                                     bufferDataType);
    6269             : 
    6270          37 : lbl_next_depth:
    6271          37 :     if (dimIdx == nDimsMinus1)
    6272             :     {
    6273          25 :         auto nIters = count[dimIdx];
    6274          25 :         double *padfVal = stack[dimIdx].src_ptr;
    6275          25 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
    6276             :         while (true)
    6277             :         {
    6278          92 :             if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
    6279             :             {
    6280          88 :                 padfVal[0] = padfVal[0] * dfScale + dfOffset;
    6281          88 :                 if (bDTIsComplex)
    6282             :                 {
    6283           1 :                     padfVal[1] = padfVal[1] * dfScale + dfOffset;
    6284             :                 }
    6285          88 :                 if (bTempBufferNeeded)
    6286             :                 {
    6287          29 :                     GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
    6288             :                                                     dst_ptr, bufferDataType);
    6289             :                 }
    6290             :             }
    6291             :             else
    6292             :             {
    6293           4 :                 memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
    6294             :             }
    6295             : 
    6296          92 :             if ((--nIters) == 0)
    6297          25 :                 break;
    6298          67 :             padfVal += stack[dimIdx].src_inc_offset;
    6299          67 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    6300             :         }
    6301             :     }
    6302             :     else
    6303             :     {
    6304          12 :         stack[dimIdx].nIters = count[dimIdx];
    6305             :         while (true)
    6306             :         {
    6307          30 :             dimIdx++;
    6308          30 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    6309          30 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6310          30 :             goto lbl_next_depth;
    6311          30 :         lbl_return_to_caller:
    6312          30 :             dimIdx--;
    6313          30 :             if ((--stack[dimIdx].nIters) == 0)
    6314          12 :                 break;
    6315          18 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    6316          18 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6317             :         }
    6318             :     }
    6319          37 :     if (dimIdx > 0)
    6320          30 :         goto lbl_return_to_caller;
    6321             : 
    6322           7 :     if (bTempBufferNeeded)
    6323           2 :         VSIFree(pTempBuffer);
    6324           7 :     return true;
    6325             : }
    6326             : 
    6327             : /************************************************************************/
    6328             : /*                             IWrite()                                 */
    6329             : /************************************************************************/
    6330             : 
    6331          16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
    6332             :                                  const size_t *count, const GInt64 *arrayStep,
    6333             :                                  const GPtrDiff_t *bufferStride,
    6334             :                                  const GDALExtendedDataType &bufferDataType,
    6335             :                                  const void *pSrcBuffer)
    6336             : {
    6337          16 :     const double dfScale = m_dfScale;
    6338          16 :     const double dfOffset = m_dfOffset;
    6339          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
    6340             :     const auto dtDouble =
    6341          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
    6342          16 :     const size_t nDTSize = dtDouble.GetSize();
    6343          16 :     const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
    6344             :     const bool bSelfAndParentHaveNoData =
    6345          16 :         m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
    6346          16 :     double dfNoData = 0;
    6347          16 :     if (m_bHasNoData)
    6348             :     {
    6349           7 :         GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
    6350             :                         &dfNoData, GDT_Float64, 0, 1);
    6351             :     }
    6352             : 
    6353          16 :     double adfSrcNoData[2] = {0, 0};
    6354          16 :     if (bSelfAndParentHaveNoData)
    6355             :     {
    6356           7 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
    6357           7 :                                         m_poParent->GetDataType(),
    6358             :                                         &adfSrcNoData[0], dtDouble);
    6359             :     }
    6360             : 
    6361          16 :     const auto nDims = GetDimensions().size();
    6362          16 :     if (nDims == 0)
    6363             :     {
    6364             :         double adfVal[2];
    6365          10 :         GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
    6366             :                                         dtDouble);
    6367          16 :         if (bSelfAndParentHaveNoData &&
    6368           6 :             (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
    6369             :         {
    6370           4 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
    6371           2 :                                      bufferStride, m_poParent->GetDataType(),
    6372           4 :                                      m_poParent->GetRawNoDataValue());
    6373             :         }
    6374             :         else
    6375             :         {
    6376           8 :             adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
    6377           8 :             if (bDTIsComplex)
    6378             :             {
    6379           2 :                 adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
    6380             :             }
    6381           8 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
    6382           8 :                                      bufferStride, dtDouble, &adfVal[0]);
    6383             :         }
    6384             :     }
    6385             : 
    6386          12 :     std::vector<GPtrDiff_t> tmpBufferStrideVector;
    6387           6 :     size_t nElts = 1;
    6388           6 :     tmpBufferStrideVector.resize(nDims);
    6389          20 :     for (size_t i = 0; i < nDims; i++)
    6390          14 :         nElts *= count[i];
    6391           6 :     tmpBufferStrideVector.back() = 1;
    6392          14 :     for (size_t i = nDims - 1; i > 0;)
    6393             :     {
    6394           8 :         --i;
    6395           8 :         tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
    6396             :     }
    6397           6 :     const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
    6398           6 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
    6399           6 :     if (!pTempBuffer)
    6400           0 :         return false;
    6401             : 
    6402             :     struct Stack
    6403             :     {
    6404             :         size_t nIters = 0;
    6405             :         double *dst_ptr = nullptr;
    6406             :         const GByte *src_ptr = nullptr;
    6407             :         GPtrDiff_t src_inc_offset = 0;
    6408             :         GPtrDiff_t dst_inc_offset = 0;
    6409             :     };
    6410             : 
    6411           6 :     std::vector<Stack> stack(nDims);
    6412           6 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    6413          20 :     for (size_t i = 0; i < nDims; i++)
    6414             :     {
    6415          28 :         stack[i].dst_inc_offset =
    6416          14 :             tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
    6417          14 :         stack[i].src_inc_offset =
    6418          14 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6419             :     }
    6420           6 :     stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
    6421           6 :     stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
    6422             : 
    6423           6 :     size_t dimIdx = 0;
    6424           6 :     const size_t nDimsMinus1 = nDims - 1;
    6425             : 
    6426          34 : lbl_next_depth:
    6427          34 :     if (dimIdx == nDimsMinus1)
    6428             :     {
    6429          23 :         auto nIters = count[dimIdx];
    6430          23 :         double *dst_ptr = stack[dimIdx].dst_ptr;
    6431          23 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
    6432             :         while (true)
    6433             :         {
    6434             :             double adfVal[2];
    6435             :             const double *padfSrcVal;
    6436          86 :             if (bIsBufferDataTypeNativeDataType)
    6437             :             {
    6438          50 :                 padfSrcVal = reinterpret_cast<const double *>(src_ptr);
    6439             :             }
    6440             :             else
    6441             :             {
    6442          36 :                 GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
    6443             :                                                 &adfVal[0], dtDouble);
    6444          36 :                 padfSrcVal = adfVal;
    6445             :             }
    6446             : 
    6447         148 :             if (bSelfAndParentHaveNoData &&
    6448          62 :                 (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
    6449             :             {
    6450           3 :                 dst_ptr[0] = adfSrcNoData[0];
    6451           3 :                 if (bDTIsComplex)
    6452             :                 {
    6453           1 :                     dst_ptr[1] = adfSrcNoData[1];
    6454             :                 }
    6455             :             }
    6456             :             else
    6457             :             {
    6458          83 :                 dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
    6459          83 :                 if (bDTIsComplex)
    6460             :                 {
    6461           1 :                     dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
    6462             :                 }
    6463             :             }
    6464             : 
    6465          86 :             if ((--nIters) == 0)
    6466          23 :                 break;
    6467          63 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    6468          63 :             src_ptr += stack[dimIdx].src_inc_offset;
    6469          63 :         }
    6470             :     }
    6471             :     else
    6472             :     {
    6473          11 :         stack[dimIdx].nIters = count[dimIdx];
    6474             :         while (true)
    6475             :         {
    6476          28 :             dimIdx++;
    6477          28 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    6478          28 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6479          28 :             goto lbl_next_depth;
    6480          28 :         lbl_return_to_caller:
    6481          28 :             dimIdx--;
    6482          28 :             if ((--stack[dimIdx].nIters) == 0)
    6483          11 :                 break;
    6484          17 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    6485          17 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6486             :         }
    6487             :     }
    6488          34 :     if (dimIdx > 0)
    6489          28 :         goto lbl_return_to_caller;
    6490             : 
    6491             :     // If the parent array is not double/complex-double, then convert the
    6492             :     // values to it, before calling Write(), as some implementations can be
    6493             :     // very slow when doing the type conversion.
    6494           6 :     const auto &eParentDT = m_poParent->GetDataType();
    6495           6 :     const size_t nParentDTSize = eParentDT.GetSize();
    6496           6 :     if (nParentDTSize <= nDTSize / 2)
    6497             :     {
    6498             :         // Copy in-place by making sure that source and target do not overlap
    6499           6 :         const auto eNumericDT = dtDouble.GetNumericDataType();
    6500           6 :         const auto eParentNumericDT = eParentDT.GetNumericDataType();
    6501             : 
    6502             :         // Copy first element
    6503             :         {
    6504           6 :             std::vector<GByte> abyTemp(nParentDTSize);
    6505           6 :             GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
    6506           6 :                             static_cast<int>(nDTSize), &abyTemp[0],
    6507             :                             eParentNumericDT, static_cast<int>(nParentDTSize),
    6508             :                             1);
    6509           6 :             memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
    6510             :         }
    6511             :         // Remaining elements
    6512          86 :         for (size_t i = 1; i < nElts; ++i)
    6513             :         {
    6514          80 :             GDALCopyWords64(
    6515          80 :                 static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
    6516          80 :                 static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
    6517             :                 eParentNumericDT, 0, 1);
    6518             :         }
    6519             :     }
    6520             : 
    6521             :     const bool ret =
    6522           6 :         m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
    6523             :                           eParentDT, pTempBuffer);
    6524             : 
    6525           6 :     VSIFree(pTempBuffer);
    6526           6 :     return ret;
    6527             : }
    6528             : 
    6529             : /************************************************************************/
    6530             : /*                           GetUnscaled()                              */
    6531             : /************************************************************************/
    6532             : 
    6533             : /** Return an array that is the unscaled version of the current one.
    6534             :  *
    6535             :  * That is each value of the unscaled array will be
    6536             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    6537             :  *
    6538             :  * Starting with GDAL 3.3, the Write() method is implemented and will convert
    6539             :  * from unscaled values to raw values.
    6540             :  *
    6541             :  * This is the same as the C function GDALMDArrayGetUnscaled().
    6542             :  *
    6543             :  * @param dfOverriddenScale Custom scale value instead of GetScale()
    6544             :  * @param dfOverriddenOffset Custom offset value instead of GetOffset()
    6545             :  * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
    6546             :  * @return a new array, that holds a reference to the original one, and thus is
    6547             :  * a view of it (not a copy), or nullptr in case of error.
    6548             :  */
    6549             : std::shared_ptr<GDALMDArray>
    6550          17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
    6551             :                          double dfOverriddenDstNodata) const
    6552             : {
    6553          34 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    6554          17 :     if (!self)
    6555             :     {
    6556           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6557             :                  "Driver implementation issue: m_pSelf not set !");
    6558           0 :         return nullptr;
    6559             :     }
    6560          17 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    6561             :     {
    6562           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6563             :                  "GetUnscaled() only supports numeric data type");
    6564           0 :         return nullptr;
    6565             :     }
    6566             :     const double dfScale =
    6567          17 :         std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
    6568             :     const double dfOffset =
    6569          17 :         std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
    6570          17 :     if (dfScale == 1.0 && dfOffset == 0.0)
    6571           4 :         return self;
    6572             : 
    6573          13 :     GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
    6574          13 :                            ? GDT_CFloat64
    6575          13 :                            : GDT_Float64;
    6576          13 :     if (dfOverriddenScale == -1 && dfOverriddenOffset == 0)
    6577             :     {
    6578           1 :         if (GetDataType().GetNumericDataType() == GDT_Float16)
    6579           0 :             eDT = GDT_Float16;
    6580           1 :         if (GetDataType().GetNumericDataType() == GDT_Float32)
    6581           1 :             eDT = GDT_Float32;
    6582             :     }
    6583             : 
    6584          26 :     return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
    6585          13 :                                        dfOverriddenDstNodata, eDT);
    6586             : }
    6587             : 
    6588             : /************************************************************************/
    6589             : /*                         GDALMDArrayMask                              */
    6590             : /************************************************************************/
    6591             : 
    6592             : class GDALMDArrayMask final : public GDALPamMDArray
    6593             : {
    6594             :   private:
    6595             :     std::shared_ptr<GDALMDArray> m_poParent{};
    6596             :     GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_Byte)};
    6597             :     double m_dfMissingValue = 0.0;
    6598             :     bool m_bHasMissingValue = false;
    6599             :     double m_dfFillValue = 0.0;
    6600             :     bool m_bHasFillValue = false;
    6601             :     double m_dfValidMin = 0.0;
    6602             :     bool m_bHasValidMin = false;
    6603             :     double m_dfValidMax = 0.0;
    6604             :     bool m_bHasValidMax = false;
    6605             :     std::vector<uint32_t> m_anValidFlagMasks{};
    6606             :     std::vector<uint32_t> m_anValidFlagValues{};
    6607             : 
    6608             :     bool Init(CSLConstList papszOptions);
    6609             : 
    6610             :     template <typename Type>
    6611             :     void
    6612             :     ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
    6613             :                  const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    6614             :                  const void *pTempBuffer,
    6615             :                  const GDALExtendedDataType &oTmpBufferDT,
    6616             :                  const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
    6617             : 
    6618             :   protected:
    6619          45 :     explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
    6620          90 :         : GDALAbstractMDArray(std::string(),
    6621          90 :                               "Mask of " + poParent->GetFullName()),
    6622          90 :           GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
    6623          90 :                          GDALPamMultiDim::GetPAM(poParent),
    6624             :                          poParent->GetContext()),
    6625         225 :           m_poParent(std::move(poParent))
    6626             :     {
    6627          45 :     }
    6628             : 
    6629             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6630             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    6631             :                const GDALExtendedDataType &bufferDataType,
    6632             :                void *pDstBuffer) const override;
    6633             : 
    6634           0 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6635             :                      CSLConstList papszOptions) const override
    6636             :     {
    6637           0 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
    6638             :     }
    6639             : 
    6640             :   public:
    6641             :     static std::shared_ptr<GDALMDArrayMask>
    6642             :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    6643             :            CSLConstList papszOptions);
    6644             : 
    6645           1 :     bool IsWritable() const override
    6646             :     {
    6647           1 :         return false;
    6648             :     }
    6649             : 
    6650          48 :     const std::string &GetFilename() const override
    6651             :     {
    6652          48 :         return m_poParent->GetFilename();
    6653             :     }
    6654             : 
    6655             :     const std::vector<std::shared_ptr<GDALDimension>> &
    6656         373 :     GetDimensions() const override
    6657             :     {
    6658         373 :         return m_poParent->GetDimensions();
    6659             :     }
    6660             : 
    6661         132 :     const GDALExtendedDataType &GetDataType() const override
    6662             :     {
    6663         132 :         return m_dt;
    6664             :     }
    6665             : 
    6666           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    6667             :     {
    6668           1 :         return m_poParent->GetSpatialRef();
    6669             :     }
    6670             : 
    6671           2 :     std::vector<GUInt64> GetBlockSize() const override
    6672             :     {
    6673           2 :         return m_poParent->GetBlockSize();
    6674             :     }
    6675             : };
    6676             : 
    6677             : /************************************************************************/
    6678             : /*                    GDALMDArrayMask::Create()                         */
    6679             : /************************************************************************/
    6680             : 
    6681             : /* static */ std::shared_ptr<GDALMDArrayMask>
    6682          45 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
    6683             :                         CSLConstList papszOptions)
    6684             : {
    6685          90 :     auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
    6686          45 :     newAr->SetSelf(newAr);
    6687          45 :     if (!newAr->Init(papszOptions))
    6688           6 :         return nullptr;
    6689          39 :     return newAr;
    6690             : }
    6691             : 
    6692             : /************************************************************************/
    6693             : /*                    GDALMDArrayMask::Init()                           */
    6694             : /************************************************************************/
    6695             : 
    6696          45 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
    6697             : {
    6698             :     const auto GetSingleValNumericAttr =
    6699         180 :         [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
    6700             :     {
    6701         540 :         auto poAttr = m_poParent->GetAttribute(pszAttrName);
    6702         180 :         if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
    6703             :         {
    6704          22 :             const auto anDimSizes = poAttr->GetDimensionsSize();
    6705          21 :             if (anDimSizes.empty() ||
    6706          10 :                 (anDimSizes.size() == 1 && anDimSizes[0] == 1))
    6707             :             {
    6708          11 :                 bHasVal = true;
    6709          11 :                 dfVal = poAttr->ReadAsDouble();
    6710             :             }
    6711             :         }
    6712         180 :     };
    6713             : 
    6714          45 :     GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
    6715          45 :                             m_dfMissingValue);
    6716          45 :     GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
    6717          45 :     GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
    6718          45 :     GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
    6719             : 
    6720             :     {
    6721         135 :         auto poValidRange = m_poParent->GetAttribute("valid_range");
    6722          50 :         if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
    6723          55 :             poValidRange->GetDimensionsSize()[0] == 2 &&
    6724           5 :             poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
    6725             :         {
    6726           5 :             m_bHasValidMin = true;
    6727           5 :             m_bHasValidMax = true;
    6728           5 :             auto vals = poValidRange->ReadAsDoubleArray();
    6729           5 :             CPLAssert(vals.size() == 2);
    6730           5 :             m_dfValidMin = vals[0];
    6731           5 :             m_dfValidMax = vals[1];
    6732             :         }
    6733             :     }
    6734             : 
    6735             :     // Take into account
    6736             :     // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
    6737             :     // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
    6738             :     const char *pszUnmaskFlags =
    6739          45 :         CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
    6740          45 :     if (pszUnmaskFlags)
    6741             :     {
    6742             :         const auto IsScalarStringAttr =
    6743          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
    6744             :         {
    6745          26 :             return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
    6746          26 :                    (poAttr->GetDimensionsSize().empty() ||
    6747          13 :                     (poAttr->GetDimensionsSize().size() == 1 &&
    6748          26 :                      poAttr->GetDimensionsSize()[0] == 1));
    6749             :         };
    6750             : 
    6751          28 :         auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
    6752          14 :         if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
    6753             :         {
    6754           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6755             :                      "UNMASK_FLAGS option specified but array has no "
    6756             :                      "flag_meanings attribute");
    6757           1 :             return false;
    6758             :         }
    6759          13 :         const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
    6760          13 :         if (!pszFlagMeanings)
    6761             :         {
    6762           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6763             :                      "Cannot read flag_meanings attribute");
    6764           1 :             return false;
    6765             :         }
    6766             : 
    6767             :         const auto IsSingleDimNumericAttr =
    6768          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
    6769             :         {
    6770          26 :             return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
    6771          26 :                    poAttr->GetDimensionsSize().size() == 1;
    6772             :         };
    6773             : 
    6774          24 :         auto poFlagValues = m_poParent->GetAttribute("flag_values");
    6775             :         const bool bHasFlagValues =
    6776          12 :             poFlagValues && IsSingleDimNumericAttr(poFlagValues);
    6777             : 
    6778          24 :         auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
    6779             :         const bool bHasFlagMasks =
    6780          12 :             poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
    6781             : 
    6782          12 :         if (!bHasFlagValues && !bHasFlagMasks)
    6783             :         {
    6784           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6785             :                      "Cannot find flag_values and/or flag_masks attribute");
    6786           1 :             return false;
    6787             :         }
    6788             : 
    6789             :         const CPLStringList aosUnmaskFlags(
    6790          11 :             CSLTokenizeString2(pszUnmaskFlags, ",", 0));
    6791             :         const CPLStringList aosFlagMeanings(
    6792          11 :             CSLTokenizeString2(pszFlagMeanings, " ", 0));
    6793             : 
    6794          11 :         if (bHasFlagValues)
    6795             :         {
    6796           7 :             const auto eType = poFlagValues->GetDataType().GetNumericDataType();
    6797             :             // We could support Int64 or UInt64, but more work...
    6798           7 :             if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
    6799           0 :                 eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
    6800             :             {
    6801           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    6802             :                          "Unsupported data type for flag_values attribute: %s",
    6803             :                          GDALGetDataTypeName(eType));
    6804           0 :                 return false;
    6805             :             }
    6806             :         }
    6807             : 
    6808          11 :         if (bHasFlagMasks)
    6809             :         {
    6810           6 :             const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
    6811             :             // We could support Int64 or UInt64, but more work...
    6812           6 :             if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
    6813           0 :                 eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
    6814             :             {
    6815           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    6816             :                          "Unsupported data type for flag_masks attribute: %s",
    6817             :                          GDALGetDataTypeName(eType));
    6818           0 :                 return false;
    6819             :             }
    6820             :         }
    6821             : 
    6822             :         const std::vector<double> adfValues(
    6823             :             bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
    6824          11 :                            : std::vector<double>());
    6825             :         const std::vector<double> adfMasks(
    6826             :             bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
    6827          11 :                           : std::vector<double>());
    6828             : 
    6829          18 :         if (bHasFlagValues &&
    6830           7 :             adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
    6831             :         {
    6832           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6833             :                      "Number of values in flag_values attribute is different "
    6834             :                      "from the one in flag_meanings");
    6835           1 :             return false;
    6836             :         }
    6837             : 
    6838          16 :         if (bHasFlagMasks &&
    6839           6 :             adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
    6840             :         {
    6841           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6842             :                      "Number of values in flag_masks attribute is different "
    6843             :                      "from the one in flag_meanings");
    6844           1 :             return false;
    6845             :         }
    6846             : 
    6847          19 :         for (int i = 0; i < aosUnmaskFlags.size(); ++i)
    6848             :         {
    6849          11 :             const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
    6850          11 :             if (nIdxFlag < 0)
    6851             :             {
    6852           1 :                 CPLError(
    6853             :                     CE_Failure, CPLE_AppDefined,
    6854             :                     "Cannot fing flag %s in flag_meanings = '%s' attribute",
    6855             :                     aosUnmaskFlags[i], pszFlagMeanings);
    6856           1 :                 return false;
    6857             :             }
    6858             : 
    6859          10 :             if (bHasFlagValues && adfValues[nIdxFlag] < 0)
    6860             :             {
    6861           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    6862             :                          "Invalid value in flag_values[%d] = %f", nIdxFlag,
    6863           0 :                          adfValues[nIdxFlag]);
    6864           0 :                 return false;
    6865             :             }
    6866             : 
    6867          10 :             if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
    6868             :             {
    6869           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    6870             :                          "Invalid value in flag_masks[%d] = %f", nIdxFlag,
    6871           0 :                          adfMasks[nIdxFlag]);
    6872           0 :                 return false;
    6873             :             }
    6874             : 
    6875          10 :             if (bHasFlagValues)
    6876             :             {
    6877          12 :                 m_anValidFlagValues.push_back(
    6878           6 :                     static_cast<uint32_t>(adfValues[nIdxFlag]));
    6879             :             }
    6880             : 
    6881          10 :             if (bHasFlagMasks)
    6882             :             {
    6883          12 :                 m_anValidFlagMasks.push_back(
    6884           6 :                     static_cast<uint32_t>(adfMasks[nIdxFlag]));
    6885             :             }
    6886             :         }
    6887             :     }
    6888             : 
    6889          39 :     return true;
    6890             : }
    6891             : 
    6892             : /************************************************************************/
    6893             : /*                             IRead()                                  */
    6894             : /************************************************************************/
    6895             : 
    6896          48 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6897             :                             const GInt64 *arrayStep,
    6898             :                             const GPtrDiff_t *bufferStride,
    6899             :                             const GDALExtendedDataType &bufferDataType,
    6900             :                             void *pDstBuffer) const
    6901             : {
    6902          48 :     size_t nElts = 1;
    6903          48 :     const size_t nDims = GetDimensionCount();
    6904          96 :     std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
    6905         132 :     for (size_t i = 0; i < nDims; i++)
    6906          84 :         nElts *= count[i];
    6907          48 :     if (nDims > 0)
    6908             :     {
    6909          43 :         tmpBufferStrideVector.back() = 1;
    6910          84 :         for (size_t i = nDims - 1; i > 0;)
    6911             :         {
    6912          41 :             --i;
    6913          41 :             tmpBufferStrideVector[i] =
    6914          41 :                 tmpBufferStrideVector[i + 1] * count[i + 1];
    6915             :         }
    6916             :     }
    6917             : 
    6918             :     /* Optimized case: if we are an integer data type and that there is no */
    6919             :     /* attribute that can be used to set mask = 0, then fill the mask buffer */
    6920             :     /* directly */
    6921          46 :     if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
    6922          70 :         !m_bHasValidMax && m_anValidFlagValues.empty() &&
    6923          32 :         m_anValidFlagMasks.empty() &&
    6924         103 :         m_poParent->GetRawNoDataValue() == nullptr &&
    6925           9 :         GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
    6926             :     {
    6927           7 :         const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
    6928           7 :         if (bBufferDataTypeIsByte)  // Byte case
    6929             :         {
    6930           4 :             bool bContiguous = true;
    6931          10 :             for (size_t i = 0; i < nDims; i++)
    6932             :             {
    6933           7 :                 if (bufferStride[i] != tmpBufferStrideVector[i])
    6934             :                 {
    6935           1 :                     bContiguous = false;
    6936           1 :                     break;
    6937             :                 }
    6938             :             }
    6939           4 :             if (bContiguous)
    6940             :             {
    6941             :                 // CPLDebug("GDAL", "GetMask(): contiguous case");
    6942           3 :                 memset(pDstBuffer, 1, nElts);
    6943           3 :                 return true;
    6944             :             }
    6945             :         }
    6946             : 
    6947             :         struct Stack
    6948             :         {
    6949             :             size_t nIters = 0;
    6950             :             GByte *dst_ptr = nullptr;
    6951             :             GPtrDiff_t dst_inc_offset = 0;
    6952             :         };
    6953             : 
    6954           4 :         std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
    6955           4 :         const size_t nBufferDTSize = bufferDataType.GetSize();
    6956          13 :         for (size_t i = 0; i < nDims; i++)
    6957             :         {
    6958           9 :             stack[i].dst_inc_offset =
    6959           9 :                 static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6960             :         }
    6961           4 :         stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    6962             : 
    6963           4 :         size_t dimIdx = 0;
    6964           4 :         const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
    6965             :         GByte abyOne[16];  // 16 is sizeof GDT_CFloat64
    6966           4 :         CPLAssert(nBufferDTSize <= 16);
    6967           4 :         const GByte flag = 1;
    6968             :         // Coverity misses that m_dt is of type Byte
    6969             :         // coverity[overrun-buffer-val]
    6970           4 :         GDALExtendedDataType::CopyValue(&flag, m_dt, abyOne, bufferDataType);
    6971             : 
    6972          28 :     lbl_next_depth:
    6973          28 :         if (dimIdx == nDimsMinus1)
    6974             :         {
    6975          19 :             auto nIters = nDims > 0 ? count[dimIdx] : 1;
    6976          19 :             GByte *dst_ptr = stack[dimIdx].dst_ptr;
    6977             : 
    6978             :             while (true)
    6979             :             {
    6980             :                 // cppcheck-suppress knownConditionTrueFalse
    6981          73 :                 if (bBufferDataTypeIsByte)
    6982             :                 {
    6983          24 :                     *dst_ptr = flag;
    6984             :                 }
    6985             :                 else
    6986             :                 {
    6987          49 :                     memcpy(dst_ptr, abyOne, nBufferDTSize);
    6988             :                 }
    6989             : 
    6990          73 :                 if ((--nIters) == 0)
    6991          19 :                     break;
    6992          54 :                 dst_ptr += stack[dimIdx].dst_inc_offset;
    6993             :             }
    6994             :         }
    6995             :         else
    6996             :         {
    6997           9 :             stack[dimIdx].nIters = count[dimIdx];
    6998             :             while (true)
    6999             :             {
    7000          24 :                 dimIdx++;
    7001          24 :                 stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    7002          24 :                 goto lbl_next_depth;
    7003          24 :             lbl_return_to_caller:
    7004          24 :                 dimIdx--;
    7005          24 :                 if ((--stack[dimIdx].nIters) == 0)
    7006           9 :                     break;
    7007          15 :                 stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    7008             :             }
    7009             :         }
    7010          28 :         if (dimIdx > 0)
    7011          24 :             goto lbl_return_to_caller;
    7012             : 
    7013           4 :         return true;
    7014             :     }
    7015             : 
    7016             :     const auto oTmpBufferDT =
    7017          41 :         GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
    7018             :             ? GDALExtendedDataType::Create(GDT_Float64)
    7019          82 :             : m_poParent->GetDataType();
    7020          41 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
    7021          41 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
    7022          41 :     if (!pTempBuffer)
    7023           0 :         return false;
    7024          82 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
    7025          41 :                           tmpBufferStrideVector.data(), oTmpBufferDT,
    7026             :                           pTempBuffer))
    7027             :     {
    7028           0 :         VSIFree(pTempBuffer);
    7029           0 :         return false;
    7030             :     }
    7031             : 
    7032          41 :     switch (oTmpBufferDT.GetNumericDataType())
    7033             :     {
    7034           6 :         case GDT_Byte:
    7035           6 :             ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
    7036             :                                 pTempBuffer, oTmpBufferDT,
    7037             :                                 tmpBufferStrideVector);
    7038           6 :             break;
    7039             : 
    7040           0 :         case GDT_Int8:
    7041           0 :             ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
    7042             :                                 pTempBuffer, oTmpBufferDT,
    7043             :                                 tmpBufferStrideVector);
    7044           0 :             break;
    7045             : 
    7046           1 :         case GDT_UInt16:
    7047           1 :             ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
    7048             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
    7049             :                                   tmpBufferStrideVector);
    7050           1 :             break;
    7051             : 
    7052          14 :         case GDT_Int16:
    7053          14 :             ReadInternal<GInt16>(count, bufferStride, bufferDataType,
    7054             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7055             :                                  tmpBufferStrideVector);
    7056          14 :             break;
    7057             : 
    7058           1 :         case GDT_UInt32:
    7059           1 :             ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
    7060             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
    7061             :                                   tmpBufferStrideVector);
    7062           1 :             break;
    7063             : 
    7064           5 :         case GDT_Int32:
    7065           5 :             ReadInternal<GInt32>(count, bufferStride, bufferDataType,
    7066             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7067             :                                  tmpBufferStrideVector);
    7068           5 :             break;
    7069             : 
    7070           0 :         case GDT_UInt64:
    7071           0 :             ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
    7072             :                                         pDstBuffer, pTempBuffer, oTmpBufferDT,
    7073             :                                         tmpBufferStrideVector);
    7074           0 :             break;
    7075             : 
    7076           0 :         case GDT_Int64:
    7077           0 :             ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
    7078             :                                        pDstBuffer, pTempBuffer, oTmpBufferDT,
    7079             :                                        tmpBufferStrideVector);
    7080           0 :             break;
    7081             : 
    7082           0 :         case GDT_Float16:
    7083           0 :             ReadInternal<GFloat16>(count, bufferStride, bufferDataType,
    7084             :                                    pDstBuffer, pTempBuffer, oTmpBufferDT,
    7085             :                                    tmpBufferStrideVector);
    7086           0 :             break;
    7087             : 
    7088           7 :         case GDT_Float32:
    7089           7 :             ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
    7090             :                                 pTempBuffer, oTmpBufferDT,
    7091             :                                 tmpBufferStrideVector);
    7092           7 :             break;
    7093             : 
    7094           7 :         case GDT_Float64:
    7095           7 :             ReadInternal<double>(count, bufferStride, bufferDataType,
    7096             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7097             :                                  tmpBufferStrideVector);
    7098           7 :             break;
    7099           0 :         case GDT_Unknown:
    7100             :         case GDT_CInt16:
    7101             :         case GDT_CInt32:
    7102             :         case GDT_CFloat16:
    7103             :         case GDT_CFloat32:
    7104             :         case GDT_CFloat64:
    7105             :         case GDT_TypeCount:
    7106           0 :             CPLAssert(false);
    7107             :             break;
    7108             :     }
    7109             : 
    7110          41 :     VSIFree(pTempBuffer);
    7111             : 
    7112          41 :     return true;
    7113             : }
    7114             : 
    7115             : /************************************************************************/
    7116             : /*                          IsValidForDT()                              */
    7117             : /************************************************************************/
    7118             : 
    7119          38 : template <typename Type> static bool IsValidForDT(double dfVal)
    7120             : {
    7121          38 :     if (std::isnan(dfVal))
    7122           0 :         return false;
    7123          38 :     if (dfVal < static_cast<double>(cpl::NumericLimits<Type>::lowest()))
    7124           0 :         return false;
    7125          38 :     if (dfVal > static_cast<double>(cpl::NumericLimits<Type>::max()))
    7126           0 :         return false;
    7127          38 :     return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
    7128             : }
    7129             : 
    7130           9 : template <> bool IsValidForDT<double>(double)
    7131             : {
    7132           9 :     return true;
    7133             : }
    7134             : 
    7135             : /************************************************************************/
    7136             : /*                              IsNan()                                 */
    7137             : /************************************************************************/
    7138             : 
    7139        1038 : template <typename Type> inline bool IsNan(Type)
    7140             : {
    7141        1038 :     return false;
    7142             : }
    7143             : 
    7144          25 : template <> bool IsNan<double>(double val)
    7145             : {
    7146          25 :     return std::isnan(val);
    7147             : }
    7148             : 
    7149          26 : template <> bool IsNan<float>(float val)
    7150             : {
    7151          26 :     return std::isnan(val);
    7152             : }
    7153             : 
    7154             : /************************************************************************/
    7155             : /*                         ReadInternal()                               */
    7156             : /************************************************************************/
    7157             : 
    7158             : template <typename Type>
    7159          41 : void GDALMDArrayMask::ReadInternal(
    7160             :     const size_t *count, const GPtrDiff_t *bufferStride,
    7161             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    7162             :     const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
    7163             :     const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
    7164             : {
    7165          41 :     const size_t nDims = GetDimensionCount();
    7166             : 
    7167         205 :     const auto castValue = [](bool &bHasVal, double dfVal) -> Type
    7168             :     {
    7169         205 :         if (bHasVal)
    7170             :         {
    7171          47 :             if (IsValidForDT<Type>(dfVal))
    7172             :             {
    7173          47 :                 return static_cast<Type>(dfVal);
    7174             :             }
    7175             :             else
    7176             :             {
    7177           0 :                 bHasVal = false;
    7178             :             }
    7179             :         }
    7180         158 :         return 0;
    7181             :     };
    7182             : 
    7183          41 :     const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
    7184          41 :     bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
    7185             :     const Type nNoDataValue =
    7186          41 :         castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
    7187          41 :     bool bHasMissingValue = m_bHasMissingValue;
    7188          41 :     const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
    7189          41 :     bool bHasFillValue = m_bHasFillValue;
    7190          41 :     const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
    7191          41 :     bool bHasValidMin = m_bHasValidMin;
    7192          41 :     const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
    7193          41 :     bool bHasValidMax = m_bHasValidMax;
    7194          41 :     const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
    7195          41 :     const bool bHasValidFlags =
    7196          41 :         !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
    7197             : 
    7198         348 :     const auto IsValidFlag = [this](Type v)
    7199             :     {
    7200          54 :         if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
    7201             :         {
    7202          20 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
    7203             :             {
    7204          12 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
    7205             :                     m_anValidFlagValues[i])
    7206             :                 {
    7207           4 :                     return true;
    7208             :                 }
    7209             :             }
    7210             :         }
    7211          42 :         else if (!m_anValidFlagValues.empty())
    7212             :         {
    7213          49 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
    7214             :             {
    7215          29 :                 if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
    7216             :                 {
    7217           4 :                     return true;
    7218             :                 }
    7219             :             }
    7220             :         }
    7221             :         else /* if( !m_anValidFlagMasks.empty() ) */
    7222             :         {
    7223          31 :             for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
    7224             :             {
    7225          22 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
    7226             :                 {
    7227           9 :                     return true;
    7228             :                 }
    7229             :             }
    7230             :         }
    7231          37 :         return false;
    7232             :     };
    7233             : 
    7234             : #define GET_MASK_FOR_SAMPLE(v)                                                 \
    7235             :     static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
    7236             :                        !(bHasMissingValue && v == nMissingValue) &&            \
    7237             :                        !(bHasFillValue && v == nFillValue) &&                  \
    7238             :                        !(bHasValidMin && v < nValidMin) &&                     \
    7239             :                        !(bHasValidMax && v > nValidMax) &&                     \
    7240             :                        (!bHasValidFlags || IsValidFlag(v)));
    7241             : 
    7242          41 :     const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
    7243             :     /* Optimized case: Byte output and output buffer is contiguous */
    7244          41 :     if (bBufferDataTypeIsByte)
    7245             :     {
    7246          37 :         bool bContiguous = true;
    7247          96 :         for (size_t i = 0; i < nDims; i++)
    7248             :         {
    7249          60 :             if (bufferStride[i] != tmpBufferStrideVector[i])
    7250             :             {
    7251           1 :                 bContiguous = false;
    7252           1 :                 break;
    7253             :             }
    7254             :         }
    7255          37 :         if (bContiguous)
    7256             :         {
    7257          36 :             size_t nElts = 1;
    7258          95 :             for (size_t i = 0; i < nDims; i++)
    7259          59 :                 nElts *= count[i];
    7260             : 
    7261         670 :             for (size_t i = 0; i < nElts; i++)
    7262             :             {
    7263         634 :                 const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
    7264         634 :                 static_cast<GByte *>(pDstBuffer)[i] =
    7265         634 :                     GET_MASK_FOR_SAMPLE(*pSrc);
    7266             :             }
    7267          36 :             return;
    7268             :         }
    7269             :     }
    7270             : 
    7271           5 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
    7272             : 
    7273             :     struct Stack
    7274             :     {
    7275             :         size_t nIters = 0;
    7276             :         const GByte *src_ptr = nullptr;
    7277             :         GByte *dst_ptr = nullptr;
    7278             :         GPtrDiff_t src_inc_offset = 0;
    7279             :         GPtrDiff_t dst_inc_offset = 0;
    7280             :     };
    7281             : 
    7282          10 :     std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
    7283           5 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    7284          15 :     for (size_t i = 0; i < nDims; i++)
    7285             :     {
    7286          20 :         stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
    7287          10 :             tmpBufferStrideVector[i] * nTmpBufferDTSize);
    7288          10 :         stack[i].dst_inc_offset =
    7289          10 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    7290             :     }
    7291           5 :     stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
    7292           5 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    7293             : 
    7294           5 :     size_t dimIdx = 0;
    7295           5 :     const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
    7296             :     GByte abyZeroOrOne[2][16];  // 16 is sizeof GDT_CFloat64
    7297           5 :     CPLAssert(nBufferDTSize <= 16);
    7298          15 :     for (GByte flag = 0; flag <= 1; flag++)
    7299             :     {
    7300             :         // Coverity misses that m_dt is of type Byte
    7301             :         // coverity[overrun-buffer-val]
    7302          10 :         GDALExtendedDataType::CopyValue(&flag, m_dt, abyZeroOrOne[flag],
    7303             :                                         bufferDataType);
    7304             :     }
    7305             : 
    7306          43 : lbl_next_depth:
    7307          43 :     if (dimIdx == nDimsMinus1)
    7308             :     {
    7309          35 :         auto nIters = nDims > 0 ? count[dimIdx] : 1;
    7310          35 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
    7311          35 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
    7312             : 
    7313         420 :         while (true)
    7314             :         {
    7315         455 :             const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
    7316         455 :             const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
    7317             : 
    7318         455 :             if (bBufferDataTypeIsByte)
    7319             :             {
    7320          24 :                 *dst_ptr = flag;
    7321             :             }
    7322             :             else
    7323             :             {
    7324         431 :                 memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
    7325             :             }
    7326             : 
    7327         455 :             if ((--nIters) == 0)
    7328          35 :                 break;
    7329         420 :             src_ptr += stack[dimIdx].src_inc_offset;
    7330         420 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    7331             :         }
    7332             :     }
    7333             :     else
    7334             :     {
    7335           8 :         stack[dimIdx].nIters = count[dimIdx];
    7336             :         while (true)
    7337             :         {
    7338          38 :             dimIdx++;
    7339          38 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    7340          38 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    7341          38 :             goto lbl_next_depth;
    7342          38 :         lbl_return_to_caller:
    7343          38 :             dimIdx--;
    7344          38 :             if ((--stack[dimIdx].nIters) == 0)
    7345           8 :                 break;
    7346          30 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    7347          30 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    7348             :         }
    7349             :     }
    7350          43 :     if (dimIdx > 0)
    7351          38 :         goto lbl_return_to_caller;
    7352             : }
    7353             : 
    7354             : /************************************************************************/
    7355             : /*                            GetMask()                                 */
    7356             : /************************************************************************/
    7357             : 
    7358             : /** Return an array that is a mask for the current array
    7359             : 
    7360             :  This array will be of type Byte, with values set to 0 to indicate invalid
    7361             :  pixels of the current array, and values set to 1 to indicate valid pixels.
    7362             : 
    7363             :  The generic implementation honours the NoDataValue, as well as various
    7364             :  netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
    7365             :  and valid_range.
    7366             : 
    7367             :  Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
    7368             :  can be used to specify strings of the "flag_meanings" attribute
    7369             :  (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
    7370             :  for which pixels matching any of those flags will be set at 1 in the mask array,
    7371             :  and pixels matching none of those flags will be set at 0.
    7372             :  For example, let's consider the following netCDF variable defined with:
    7373             :  \verbatim
    7374             :  l2p_flags:valid_min = 0s ;
    7375             :  l2p_flags:valid_max = 256s ;
    7376             :  l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
    7377             :  l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
    7378             :  \endverbatim
    7379             : 
    7380             :  GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
    7381             :  - for pixel values *outside* valid_range [0,256], the mask value will be 0.
    7382             :  - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
    7383             :    will be 1.
    7384             :  - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
    7385             :    will be 0.
    7386             : 
    7387             :  This is the same as the C function GDALMDArrayGetMask().
    7388             : 
    7389             :  @param papszOptions NULL-terminated list of options, or NULL.
    7390             : 
    7391             :  @return a new array, that holds a reference to the original one, and thus is
    7392             :  a view of it (not a copy), or nullptr in case of error.
    7393             : */
    7394             : std::shared_ptr<GDALMDArray>
    7395          46 : GDALMDArray::GetMask(CSLConstList papszOptions) const
    7396             : {
    7397          92 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    7398          46 :     if (!self)
    7399             :     {
    7400           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    7401             :                  "Driver implementation issue: m_pSelf not set !");
    7402           0 :         return nullptr;
    7403             :     }
    7404          46 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    7405             :     {
    7406           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    7407             :                  "GetMask() only supports numeric data type");
    7408           1 :         return nullptr;
    7409             :     }
    7410          45 :     return GDALMDArrayMask::Create(self, papszOptions);
    7411             : }
    7412             : 
    7413             : /************************************************************************/
    7414             : /*                         IsRegularlySpaced()                          */
    7415             : /************************************************************************/
    7416             : 
    7417             : /** Returns whether an array is a 1D regularly spaced array.
    7418             :  *
    7419             :  * @param[out] dfStart     First value in the array
    7420             :  * @param[out] dfIncrement Increment/spacing between consecutive values.
    7421             :  * @return true if the array is regularly spaced.
    7422             :  */
    7423         183 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
    7424             : {
    7425         183 :     dfStart = 0;
    7426         183 :     dfIncrement = 0;
    7427         183 :     if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
    7428           0 :         return false;
    7429         183 :     const auto nSize = GetDimensions()[0]->GetSize();
    7430         183 :     if (nSize <= 1 || nSize > 10 * 1000 * 1000)
    7431           2 :         return false;
    7432             : 
    7433         181 :     size_t nCount = static_cast<size_t>(nSize);
    7434         362 :     std::vector<double> adfTmp;
    7435             :     try
    7436             :     {
    7437         181 :         adfTmp.resize(nCount);
    7438             :     }
    7439           0 :     catch (const std::exception &)
    7440             :     {
    7441           0 :         return false;
    7442             :     }
    7443             : 
    7444         181 :     GUInt64 anStart[1] = {0};
    7445         181 :     size_t anCount[1] = {nCount};
    7446             : 
    7447             :     const auto IsRegularlySpacedInternal =
    7448       83542 :         [&dfStart, &dfIncrement, &anCount, &adfTmp]()
    7449             :     {
    7450         253 :         dfStart = adfTmp[0];
    7451         253 :         dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
    7452         253 :         if (dfIncrement == 0)
    7453             :         {
    7454           3 :             return false;
    7455             :         }
    7456       20820 :         for (size_t i = 1; i < anCount[0]; i++)
    7457             :         {
    7458       20570 :             if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
    7459       20570 :                 1e-3 * fabs(dfIncrement))
    7460             :             {
    7461           0 :                 return false;
    7462             :             }
    7463             :         }
    7464         250 :         return true;
    7465         181 :     };
    7466             : 
    7467             :     // First try with the first block(s). This can avoid excessive processing
    7468             :     // time, for example with Zarr datasets.
    7469             :     // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
    7470             :     // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
    7471         181 :     const auto nBlockSize = GetBlockSize()[0];
    7472         181 :     if (nCount >= 5 && nBlockSize <= nCount / 2)
    7473             :     {
    7474             :         size_t nReducedCount =
    7475          75 :             std::max<size_t>(3, static_cast<size_t>(nBlockSize));
    7476         436 :         while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
    7477         361 :             nReducedCount *= 2;
    7478          75 :         anCount[0] = nReducedCount;
    7479          75 :         if (!Read(anStart, anCount, nullptr, nullptr,
    7480         150 :                   GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
    7481             :         {
    7482           0 :             return false;
    7483             :         }
    7484          75 :         if (!IsRegularlySpacedInternal())
    7485             :         {
    7486           3 :             return false;
    7487             :         }
    7488             : 
    7489             :         // Get next values
    7490          72 :         anStart[0] = nReducedCount;
    7491          72 :         anCount[0] = nCount - nReducedCount;
    7492             :     }
    7493             : 
    7494         178 :     if (!Read(anStart, anCount, nullptr, nullptr,
    7495         356 :               GDALExtendedDataType::Create(GDT_Float64),
    7496         178 :               &adfTmp[static_cast<size_t>(anStart[0])]))
    7497             :     {
    7498           0 :         return false;
    7499             :     }
    7500             : 
    7501         178 :     return IsRegularlySpacedInternal();
    7502             : }
    7503             : 
    7504             : /************************************************************************/
    7505             : /*                         GuessGeoTransform()                          */
    7506             : /************************************************************************/
    7507             : 
    7508             : /** Returns whether 2 specified dimensions form a geotransform
    7509             :  *
    7510             :  * @param nDimX                Index of the X axis.
    7511             :  * @param nDimY                Index of the Y axis.
    7512             :  * @param bPixelIsPoint        Whether the geotransform should be returned
    7513             :  *                             with the pixel-is-point (pixel-center) convention
    7514             :  *                             (bPixelIsPoint = true), or with the pixel-is-area
    7515             :  *                             (top left corner convention)
    7516             :  *                             (bPixelIsPoint = false)
    7517             :  * @param[out] adfGeoTransform Computed geotransform
    7518             :  * @return true if a geotransform could be computed.
    7519             :  */
    7520         215 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
    7521             :                                     bool bPixelIsPoint,
    7522             :                                     double adfGeoTransform[6]) const
    7523             : {
    7524         215 :     const auto &dims(GetDimensions());
    7525         430 :     auto poVarX = dims[nDimX]->GetIndexingVariable();
    7526         430 :     auto poVarY = dims[nDimY]->GetIndexingVariable();
    7527         215 :     double dfXStart = 0.0;
    7528         215 :     double dfXSpacing = 0.0;
    7529         215 :     double dfYStart = 0.0;
    7530         215 :     double dfYSpacing = 0.0;
    7531         491 :     if (poVarX && poVarX->GetDimensionCount() == 1 &&
    7532         276 :         poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
    7533         322 :         poVarY && poVarY->GetDimensionCount() == 1 &&
    7534          92 :         poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
    7535         440 :         poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
    7536          87 :         poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
    7537             :     {
    7538          87 :         adfGeoTransform[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
    7539          87 :         adfGeoTransform[1] = dfXSpacing;
    7540          87 :         adfGeoTransform[2] = 0;
    7541          87 :         adfGeoTransform[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
    7542          87 :         adfGeoTransform[4] = 0;
    7543          87 :         adfGeoTransform[5] = dfYSpacing;
    7544          87 :         return true;
    7545             :     }
    7546         128 :     return false;
    7547             : }
    7548             : 
    7549             : /************************************************************************/
    7550             : /*                       GDALMDArrayResampled                           */
    7551             : /************************************************************************/
    7552             : 
    7553             : class GDALMDArrayResampledDataset;
    7554             : 
    7555             : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
    7556             : {
    7557             :   protected:
    7558             :     CPLErr IReadBlock(int, int, void *) override;
    7559             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
    7560             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
    7561             :                      GDALDataType eBufType, GSpacing nPixelSpaceBuf,
    7562             :                      GSpacing nLineSpaceBuf,
    7563             :                      GDALRasterIOExtraArg *psExtraArg) override;
    7564             : 
    7565             :   public:
    7566             :     explicit GDALMDArrayResampledDatasetRasterBand(
    7567             :         GDALMDArrayResampledDataset *poDSIn);
    7568             : 
    7569             :     double GetNoDataValue(int *pbHasNoData) override;
    7570             : };
    7571             : 
    7572             : class GDALMDArrayResampledDataset final : public GDALPamDataset
    7573             : {
    7574             :     friend class GDALMDArrayResampled;
    7575             :     friend class GDALMDArrayResampledDatasetRasterBand;
    7576             : 
    7577             :     std::shared_ptr<GDALMDArray> m_poArray;
    7578             :     const size_t m_iXDim;
    7579             :     const size_t m_iYDim;
    7580             :     double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
    7581             :     bool m_bHasGT = false;
    7582             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
    7583             : 
    7584             :     std::vector<GUInt64> m_anOffset{};
    7585             :     std::vector<size_t> m_anCount{};
    7586             :     std::vector<GPtrDiff_t> m_anStride{};
    7587             : 
    7588             :     std::string m_osFilenameLong{};
    7589             :     std::string m_osFilenameLat{};
    7590             : 
    7591             :   public:
    7592          24 :     GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
    7593             :                                 size_t iXDim, size_t iYDim)
    7594          24 :         : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
    7595          24 :           m_anOffset(m_poArray->GetDimensionCount(), 0),
    7596          24 :           m_anCount(m_poArray->GetDimensionCount(), 1),
    7597          72 :           m_anStride(m_poArray->GetDimensionCount(), 0)
    7598             :     {
    7599          24 :         const auto &dims(m_poArray->GetDimensions());
    7600             : 
    7601          24 :         nRasterYSize = static_cast<int>(
    7602          24 :             std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
    7603          24 :         nRasterXSize = static_cast<int>(
    7604          24 :             std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
    7605             : 
    7606          24 :         m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false,
    7607          24 :                                                 m_adfGeoTransform);
    7608             : 
    7609          24 :         SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
    7610          24 :     }
    7611             : 
    7612          48 :     ~GDALMDArrayResampledDataset()
    7613          24 :     {
    7614          24 :         if (!m_osFilenameLong.empty())
    7615           5 :             VSIUnlink(m_osFilenameLong.c_str());
    7616          24 :         if (!m_osFilenameLat.empty())
    7617           5 :             VSIUnlink(m_osFilenameLat.c_str());
    7618          48 :     }
    7619             : 
    7620          43 :     CPLErr GetGeoTransform(double *padfGeoTransform) override
    7621             :     {
    7622          43 :         memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
    7623          43 :         return m_bHasGT ? CE_None : CE_Failure;
    7624             :     }
    7625             : 
    7626         105 :     const OGRSpatialReference *GetSpatialRef() const override
    7627             :     {
    7628         105 :         m_poSRS = m_poArray->GetSpatialRef();
    7629         105 :         if (m_poSRS)
    7630             :         {
    7631          79 :             m_poSRS.reset(m_poSRS->Clone());
    7632         158 :             auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
    7633         237 :             for (auto &m : axisMapping)
    7634             :             {
    7635         158 :                 if (m == static_cast<int>(m_iXDim) + 1)
    7636          79 :                     m = 1;
    7637          79 :                 else if (m == static_cast<int>(m_iYDim) + 1)
    7638          79 :                     m = 2;
    7639             :             }
    7640          79 :             m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
    7641             :         }
    7642         105 :         return m_poSRS.get();
    7643             :     }
    7644             : 
    7645           5 :     void SetGeolocationArray(const std::string &osFilenameLong,
    7646             :                              const std::string &osFilenameLat)
    7647             :     {
    7648           5 :         m_osFilenameLong = osFilenameLong;
    7649           5 :         m_osFilenameLat = osFilenameLat;
    7650          10 :         CPLStringList aosGeoLoc;
    7651           5 :         aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
    7652           5 :         aosGeoLoc.SetNameValue("LINE_STEP", "1");
    7653           5 :         aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
    7654           5 :         aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
    7655           5 :         aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG);  // FIXME?
    7656           5 :         aosGeoLoc.SetNameValue("X_BAND", "1");
    7657           5 :         aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
    7658           5 :         aosGeoLoc.SetNameValue("Y_BAND", "1");
    7659           5 :         aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
    7660           5 :         aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
    7661           5 :         SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
    7662           5 :     }
    7663             : };
    7664             : 
    7665             : /************************************************************************/
    7666             : /*                   GDALMDArrayResampledDatasetRasterBand()            */
    7667             : /************************************************************************/
    7668             : 
    7669          24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
    7670          24 :     GDALMDArrayResampledDataset *poDSIn)
    7671             : {
    7672          24 :     const auto &poArray(poDSIn->m_poArray);
    7673          24 :     const auto blockSize(poArray->GetBlockSize());
    7674          24 :     nBlockYSize = (blockSize[poDSIn->m_iYDim])
    7675          24 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    7676          13 :                                                   blockSize[poDSIn->m_iYDim]))
    7677          24 :                       : 1;
    7678          24 :     nBlockXSize = blockSize[poDSIn->m_iXDim]
    7679          13 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    7680          13 :                                                   blockSize[poDSIn->m_iXDim]))
    7681          24 :                       : poDSIn->GetRasterXSize();
    7682          24 :     eDataType = poArray->GetDataType().GetNumericDataType();
    7683          24 :     eAccess = poDSIn->eAccess;
    7684          24 : }
    7685             : 
    7686             : /************************************************************************/
    7687             : /*                           GetNoDataValue()                           */
    7688             : /************************************************************************/
    7689             : 
    7690          50 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
    7691             : {
    7692          50 :     auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
    7693          50 :     const auto &poArray(l_poDS->m_poArray);
    7694          50 :     bool bHasNodata = false;
    7695          50 :     double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
    7696          50 :     if (pbHasNoData)
    7697          46 :         *pbHasNoData = bHasNodata;
    7698          50 :     return dfRes;
    7699             : }
    7700             : 
    7701             : /************************************************************************/
    7702             : /*                            IReadBlock()                              */
    7703             : /************************************************************************/
    7704             : 
    7705           0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
    7706             :                                                          int nBlockYOff,
    7707             :                                                          void *pImage)
    7708             : {
    7709           0 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    7710           0 :     const int nXOff = nBlockXOff * nBlockXSize;
    7711           0 :     const int nYOff = nBlockYOff * nBlockYSize;
    7712           0 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    7713           0 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    7714             :     GDALRasterIOExtraArg sExtraArg;
    7715           0 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    7716           0 :     return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    7717             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    7718           0 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    7719             : }
    7720             : 
    7721             : /************************************************************************/
    7722             : /*                            IRasterIO()                               */
    7723             : /************************************************************************/
    7724             : 
    7725          32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
    7726             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
    7727             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
    7728             :     GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
    7729             :     GDALRasterIOExtraArg *psExtraArg)
    7730             : {
    7731          32 :     auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
    7732          32 :     const auto &poArray(l_poDS->m_poArray);
    7733          32 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
    7734          32 :     if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
    7735          32 :         nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
    7736          32 :         (nLineSpaceBuf % nBufferDTSize) == 0)
    7737             :     {
    7738          32 :         l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
    7739          32 :         l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
    7740          64 :         l_poDS->m_anStride[l_poDS->m_iXDim] =
    7741          32 :             static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
    7742             : 
    7743          32 :         l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
    7744          32 :         l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
    7745          64 :         l_poDS->m_anStride[l_poDS->m_iYDim] =
    7746          32 :             static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
    7747             : 
    7748          64 :         return poArray->Read(l_poDS->m_anOffset.data(),
    7749          32 :                              l_poDS->m_anCount.data(), nullptr,
    7750          32 :                              l_poDS->m_anStride.data(),
    7751          64 :                              GDALExtendedDataType::Create(eBufType), pData)
    7752          32 :                    ? CE_None
    7753          32 :                    : CE_Failure;
    7754             :     }
    7755           0 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    7756             :                                      pData, nBufXSize, nBufYSize, eBufType,
    7757           0 :                                      nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
    7758             : }
    7759             : 
    7760             : class GDALMDArrayResampled final : public GDALPamMDArray
    7761             : {
    7762             :   private:
    7763             :     std::shared_ptr<GDALMDArray> m_poParent{};
    7764             :     std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
    7765             :     std::vector<GUInt64> m_anBlockSize;
    7766             :     GDALExtendedDataType m_dt;
    7767             :     std::shared_ptr<OGRSpatialReference> m_poSRS{};
    7768             :     std::shared_ptr<GDALMDArray> m_poVarX{};
    7769             :     std::shared_ptr<GDALMDArray> m_poVarY{};
    7770             :     std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
    7771             :     std::unique_ptr<GDALDataset> m_poReprojectedDS{};
    7772             : 
    7773             :   protected:
    7774          21 :     GDALMDArrayResampled(
    7775             :         const std::shared_ptr<GDALMDArray> &poParent,
    7776             :         const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
    7777             :         const std::vector<GUInt64> &anBlockSize)
    7778          42 :         : GDALAbstractMDArray(std::string(),
    7779          42 :                               "Resampled view of " + poParent->GetFullName()),
    7780             :           GDALPamMDArray(
    7781          42 :               std::string(), "Resampled view of " + poParent->GetFullName(),
    7782          42 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
    7783          21 :           m_poParent(std::move(poParent)), m_apoDims(apoDims),
    7784         105 :           m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
    7785             :     {
    7786          21 :         CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
    7787          21 :         CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
    7788          21 :     }
    7789             : 
    7790             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    7791             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    7792             :                const GDALExtendedDataType &bufferDataType,
    7793             :                void *pDstBuffer) const override;
    7794             : 
    7795             :   public:
    7796             :     static std::shared_ptr<GDALMDArray>
    7797             :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    7798             :            const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
    7799             :            GDALRIOResampleAlg resampleAlg,
    7800             :            const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
    7801             : 
    7802          42 :     ~GDALMDArrayResampled()
    7803          21 :     {
    7804             :         // First close the warped VRT
    7805          21 :         m_poReprojectedDS.reset();
    7806          21 :         m_poParentDS.reset();
    7807          42 :     }
    7808             : 
    7809          11 :     bool IsWritable() const override
    7810             :     {
    7811          11 :         return false;
    7812             :     }
    7813             : 
    7814          74 :     const std::string &GetFilename() const override
    7815             :     {
    7816          74 :         return m_poParent->GetFilename();
    7817             :     }
    7818             : 
    7819             :     const std::vector<std::shared_ptr<GDALDimension>> &
    7820         257 :     GetDimensions() const override
    7821             :     {
    7822         257 :         return m_apoDims;
    7823             :     }
    7824             : 
    7825         109 :     const GDALExtendedDataType &GetDataType() const override
    7826             :     {
    7827         109 :         return m_dt;
    7828             :     }
    7829             : 
    7830          21 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    7831             :     {
    7832          21 :         return m_poSRS;
    7833             :     }
    7834             : 
    7835          12 :     std::vector<GUInt64> GetBlockSize() const override
    7836             :     {
    7837          12 :         return m_anBlockSize;
    7838             :     }
    7839             : 
    7840             :     std::shared_ptr<GDALAttribute>
    7841           1 :     GetAttribute(const std::string &osName) const override
    7842             :     {
    7843           1 :         return m_poParent->GetAttribute(osName);
    7844             :     }
    7845             : 
    7846             :     std::vector<std::shared_ptr<GDALAttribute>>
    7847          12 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    7848             :     {
    7849          12 :         return m_poParent->GetAttributes(papszOptions);
    7850             :     }
    7851             : 
    7852           1 :     const std::string &GetUnit() const override
    7853             :     {
    7854           1 :         return m_poParent->GetUnit();
    7855             :     }
    7856             : 
    7857           1 :     const void *GetRawNoDataValue() const override
    7858             :     {
    7859           1 :         return m_poParent->GetRawNoDataValue();
    7860             :     }
    7861             : 
    7862           1 :     double GetOffset(bool *pbHasOffset,
    7863             :                      GDALDataType *peStorageType) const override
    7864             :     {
    7865           1 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    7866             :     }
    7867             : 
    7868           1 :     double GetScale(bool *pbHasScale,
    7869             :                     GDALDataType *peStorageType) const override
    7870             :     {
    7871           1 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    7872             :     }
    7873             : };
    7874             : 
    7875             : /************************************************************************/
    7876             : /*                   GDALMDArrayResampled::Create()                     */
    7877             : /************************************************************************/
    7878             : 
    7879          29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
    7880             :     const std::shared_ptr<GDALMDArray> &poParent,
    7881             :     const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
    7882             :     GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
    7883             :     CSLConstList /* papszOptions */)
    7884             : {
    7885          29 :     const char *pszResampleAlg = "nearest";
    7886          29 :     bool unsupported = false;
    7887          29 :     switch (resampleAlg)
    7888             :     {
    7889          16 :         case GRIORA_NearestNeighbour:
    7890          16 :             pszResampleAlg = "nearest";
    7891          16 :             break;
    7892           2 :         case GRIORA_Bilinear:
    7893           2 :             pszResampleAlg = "bilinear";
    7894           2 :             break;
    7895           5 :         case GRIORA_Cubic:
    7896           5 :             pszResampleAlg = "cubic";
    7897           5 :             break;
    7898           1 :         case GRIORA_CubicSpline:
    7899           1 :             pszResampleAlg = "cubicspline";
    7900           1 :             break;
    7901           1 :         case GRIORA_Lanczos:
    7902           1 :             pszResampleAlg = "lanczos";
    7903           1 :             break;
    7904           1 :         case GRIORA_Average:
    7905           1 :             pszResampleAlg = "average";
    7906           1 :             break;
    7907           1 :         case GRIORA_Mode:
    7908           1 :             pszResampleAlg = "mode";
    7909           1 :             break;
    7910           1 :         case GRIORA_Gauss:
    7911           1 :             unsupported = true;
    7912           1 :             break;
    7913           0 :         case GRIORA_RESERVED_START:
    7914           0 :             unsupported = true;
    7915           0 :             break;
    7916           0 :         case GRIORA_RESERVED_END:
    7917           0 :             unsupported = true;
    7918           0 :             break;
    7919           1 :         case GRIORA_RMS:
    7920           1 :             pszResampleAlg = "rms";
    7921           1 :             break;
    7922             :     }
    7923          29 :     if (unsupported)
    7924             :     {
    7925           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    7926             :                  "Unsupported resample method for GetResampled()");
    7927           1 :         return nullptr;
    7928             :     }
    7929             : 
    7930          28 :     if (poParent->GetDimensionCount() < 2)
    7931             :     {
    7932           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    7933             :                  "GetResampled() only supports 2 dimensions or more");
    7934           1 :         return nullptr;
    7935             :     }
    7936             : 
    7937          27 :     const auto &aoParentDims = poParent->GetDimensions();
    7938          27 :     if (apoNewDimsIn.size() != aoParentDims.size())
    7939             :     {
    7940           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    7941             :                  "GetResampled(): apoNewDims size should be the same as "
    7942             :                  "GetDimensionCount()");
    7943           2 :         return nullptr;
    7944             :     }
    7945             : 
    7946          50 :     std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
    7947          25 :     apoNewDims.reserve(apoNewDimsIn.size());
    7948             : 
    7949          50 :     std::vector<GUInt64> anBlockSize;
    7950          25 :     anBlockSize.reserve(apoNewDimsIn.size());
    7951          50 :     const auto &anParentBlockSize = poParent->GetBlockSize();
    7952             : 
    7953          50 :     auto apoParentDims = poParent->GetDimensions();
    7954             :     // Special case for NASA EMIT datasets
    7955          30 :     const bool bYXBandOrder = (apoParentDims.size() == 3 &&
    7956           7 :                                apoParentDims[0]->GetName() == "downtrack" &&
    7957          32 :                                apoParentDims[1]->GetName() == "crosstrack" &&
    7958           2 :                                apoParentDims[2]->GetName() == "bands");
    7959             : 
    7960             :     const size_t iYDimParent =
    7961          25 :         bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
    7962             :     const size_t iXDimParent =
    7963          25 :         bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
    7964             : 
    7965          77 :     for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
    7966             :     {
    7967          53 :         if (i == iYDimParent || i == iXDimParent)
    7968          48 :             continue;
    7969           5 :         if (apoNewDimsIn[i] == nullptr)
    7970             :         {
    7971           3 :             apoNewDims.emplace_back(aoParentDims[i]);
    7972             :         }
    7973           3 :         else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
    7974           1 :                  apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
    7975             :         {
    7976           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    7977             :                      "GetResampled(): apoNewDims[%u] should be the same "
    7978             :                      "as its parent",
    7979             :                      i);
    7980           1 :             return nullptr;
    7981             :         }
    7982             :         else
    7983             :         {
    7984           1 :             apoNewDims.emplace_back(aoParentDims[i]);
    7985             :         }
    7986           4 :         anBlockSize.emplace_back(anParentBlockSize[i]);
    7987             :     }
    7988             : 
    7989             :     std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
    7990          48 :         new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
    7991             : 
    7992          24 :     double dfXStart = 0.0;
    7993          24 :     double dfXSpacing = 0.0;
    7994          24 :     bool gotXSpacing = false;
    7995          48 :     auto poNewDimX = apoNewDimsIn[iXDimParent];
    7996          24 :     if (poNewDimX)
    7997             :     {
    7998           4 :         if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
    7999             :         {
    8000           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    8001             :                      "Too big size for X dimension");
    8002           0 :             return nullptr;
    8003             :         }
    8004           4 :         auto var = poNewDimX->GetIndexingVariable();
    8005           4 :         if (var)
    8006             :         {
    8007           2 :             if (var->GetDimensionCount() != 1 ||
    8008           2 :                 var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
    8009           1 :                 !var->IsRegularlySpaced(dfXStart, dfXSpacing))
    8010             :             {
    8011           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    8012             :                          "New X dimension should be indexed by a regularly "
    8013             :                          "spaced variable");
    8014           0 :                 return nullptr;
    8015             :             }
    8016           1 :             gotXSpacing = true;
    8017             :         }
    8018             :     }
    8019             : 
    8020          24 :     double dfYStart = 0.0;
    8021          24 :     double dfYSpacing = 0.0;
    8022          48 :     auto poNewDimY = apoNewDimsIn[iYDimParent];
    8023          24 :     bool gotYSpacing = false;
    8024          24 :     if (poNewDimY)
    8025             :     {
    8026           4 :         if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
    8027             :         {
    8028           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    8029             :                      "Too big size for Y dimension");
    8030           0 :             return nullptr;
    8031             :         }
    8032           4 :         auto var = poNewDimY->GetIndexingVariable();
    8033           4 :         if (var)
    8034             :         {
    8035           2 :             if (var->GetDimensionCount() != 1 ||
    8036           2 :                 var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
    8037           1 :                 !var->IsRegularlySpaced(dfYStart, dfYSpacing))
    8038             :             {
    8039           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    8040             :                          "New Y dimension should be indexed by a regularly "
    8041             :                          "spaced variable");
    8042           0 :                 return nullptr;
    8043             :             }
    8044           1 :             gotYSpacing = true;
    8045             :         }
    8046             :     }
    8047             : 
    8048             :     // This limitation could probably be removed
    8049          24 :     if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
    8050             :     {
    8051           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    8052             :                  "Either none of new X or Y dimension should have an indexing "
    8053             :                  "variable, or both should both should have one.");
    8054           0 :         return nullptr;
    8055             :     }
    8056             : 
    8057          48 :     std::string osDstWKT;
    8058          24 :     if (poTargetSRS)
    8059             :     {
    8060           2 :         char *pszDstWKT = nullptr;
    8061           2 :         if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
    8062             :         {
    8063           0 :             CPLFree(pszDstWKT);
    8064           0 :             return nullptr;
    8065             :         }
    8066           2 :         osDstWKT = pszDstWKT;
    8067           2 :         CPLFree(pszDstWKT);
    8068             :     }
    8069             : 
    8070             :     // Use coordinate variables for geolocation array
    8071          48 :     const auto apoCoordinateVars = poParent->GetCoordinateVariables();
    8072          24 :     bool useGeolocationArray = false;
    8073          24 :     if (apoCoordinateVars.size() >= 2)
    8074             :     {
    8075           0 :         std::shared_ptr<GDALMDArray> poLongVar;
    8076           0 :         std::shared_ptr<GDALMDArray> poLatVar;
    8077          15 :         for (const auto &poCoordVar : apoCoordinateVars)
    8078             :         {
    8079          10 :             const auto &osName = poCoordVar->GetName();
    8080          30 :             const auto poAttr = poCoordVar->GetAttribute("standard_name");
    8081          20 :             std::string osStandardName;
    8082          12 :             if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
    8083           2 :                 poAttr->GetDimensionCount() == 0)
    8084             :             {
    8085           2 :                 const char *pszStandardName = poAttr->ReadAsString();
    8086           2 :                 if (pszStandardName)
    8087           2 :                     osStandardName = pszStandardName;
    8088             :             }
    8089          21 :             if (osName == "lon" || osName == "longitude" ||
    8090          21 :                 osName == "Longitude" || osStandardName == "longitude")
    8091             :             {
    8092           5 :                 poLongVar = poCoordVar;
    8093             :             }
    8094           6 :             else if (osName == "lat" || osName == "latitude" ||
    8095           6 :                      osName == "Latitude" || osStandardName == "latitude")
    8096             :             {
    8097           5 :                 poLatVar = poCoordVar;
    8098             :             }
    8099             :         }
    8100           5 :         if (poLatVar != nullptr && poLongVar != nullptr)
    8101             :         {
    8102           5 :             const auto longDimCount = poLongVar->GetDimensionCount();
    8103           5 :             const auto &longDims = poLongVar->GetDimensions();
    8104           5 :             const auto latDimCount = poLatVar->GetDimensionCount();
    8105           5 :             const auto &latDims = poLatVar->GetDimensions();
    8106           5 :             const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
    8107           5 :             const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
    8108           0 :             if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
    8109           5 :                 latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
    8110             :             {
    8111             :                 // Geolocation arrays are 1D, and of consistent size with
    8112             :                 // the variable
    8113           0 :                 useGeolocationArray = true;
    8114             :             }
    8115           1 :             else if ((longDimCount == 2 ||
    8116           6 :                       (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
    8117          10 :                      longDims[longDimCount - 2]->GetSize() == yDimSize &&
    8118          10 :                      longDims[longDimCount - 1]->GetSize() == xDimSize &&
    8119           1 :                      (latDimCount == 2 ||
    8120           6 :                       (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
    8121          15 :                      latDims[latDimCount - 2]->GetSize() == yDimSize &&
    8122           5 :                      latDims[latDimCount - 1]->GetSize() == xDimSize)
    8123             : 
    8124             :             {
    8125             :                 // Geolocation arrays are 2D (or 3D with first dimension of
    8126             :                 // size 1, as found in Sentinel 5P products), and of consistent
    8127             :                 // size with the variable
    8128           5 :                 useGeolocationArray = true;
    8129             :             }
    8130             :             else
    8131             :             {
    8132           0 :                 CPLDebug(
    8133             :                     "GDAL",
    8134             :                     "Longitude and latitude coordinate variables found, "
    8135             :                     "but their characteristics are not compatible of using "
    8136             :                     "them as geolocation arrays");
    8137             :             }
    8138           5 :             if (useGeolocationArray)
    8139             :             {
    8140          10 :                 CPLDebug("GDAL",
    8141             :                          "Setting geolocation array from variables %s and %s",
    8142           5 :                          poLongVar->GetName().c_str(),
    8143           5 :                          poLatVar->GetName().c_str());
    8144             :                 const std::string osFilenameLong =
    8145           5 :                     VSIMemGenerateHiddenFilename("longitude.tif");
    8146             :                 const std::string osFilenameLat =
    8147           5 :                     VSIMemGenerateHiddenFilename("latitude.tif");
    8148             :                 std::unique_ptr<GDALDataset> poTmpLongDS(
    8149             :                     longDimCount == 1
    8150           0 :                         ? poLongVar->AsClassicDataset(0, 0)
    8151          20 :                         : poLongVar->AsClassicDataset(longDimCount - 1,
    8152          15 :                                                       longDimCount - 2));
    8153           5 :                 auto hTIFFLongDS = GDALTranslate(
    8154             :                     osFilenameLong.c_str(),
    8155             :                     GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
    8156             :                 std::unique_ptr<GDALDataset> poTmpLatDS(
    8157           0 :                     latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
    8158          20 :                                      : poLatVar->AsClassicDataset(
    8159          15 :                                            latDimCount - 1, latDimCount - 2));
    8160           5 :                 auto hTIFFLatDS = GDALTranslate(
    8161             :                     osFilenameLat.c_str(),
    8162             :                     GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
    8163           5 :                 const bool bError =
    8164           5 :                     (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
    8165           5 :                 GDALClose(hTIFFLongDS);
    8166           5 :                 GDALClose(hTIFFLatDS);
    8167           5 :                 if (bError)
    8168             :                 {
    8169           0 :                     VSIUnlink(osFilenameLong.c_str());
    8170           0 :                     VSIUnlink(osFilenameLat.c_str());
    8171           0 :                     return nullptr;
    8172             :                 }
    8173             : 
    8174           5 :                 poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
    8175             :             }
    8176             :         }
    8177             :         else
    8178             :         {
    8179           0 :             CPLDebug("GDAL",
    8180             :                      "Coordinate variables available for %s, but "
    8181             :                      "longitude and/or latitude variables were not identified",
    8182           0 :                      poParent->GetName().c_str());
    8183             :         }
    8184             :     }
    8185             : 
    8186             :     // Build gdalwarp arguments
    8187          48 :     CPLStringList aosArgv;
    8188             : 
    8189          24 :     aosArgv.AddString("-of");
    8190          24 :     aosArgv.AddString("VRT");
    8191             : 
    8192          24 :     aosArgv.AddString("-r");
    8193          24 :     aosArgv.AddString(pszResampleAlg);
    8194             : 
    8195          24 :     if (!osDstWKT.empty())
    8196             :     {
    8197           2 :         aosArgv.AddString("-t_srs");
    8198           2 :         aosArgv.AddString(osDstWKT.c_str());
    8199             :     }
    8200             : 
    8201          24 :     if (useGeolocationArray)
    8202           5 :         aosArgv.AddString("-geoloc");
    8203             : 
    8204          24 :     if (gotXSpacing && gotYSpacing)
    8205             :     {
    8206           1 :         const double dfXMin = dfXStart - dfXSpacing / 2;
    8207             :         const double dfXMax =
    8208           1 :             dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
    8209           1 :         const double dfYMax = dfYStart - dfYSpacing / 2;
    8210             :         const double dfYMin =
    8211           1 :             dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
    8212           1 :         aosArgv.AddString("-te");
    8213           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
    8214           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
    8215           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
    8216           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
    8217             :     }
    8218             : 
    8219          24 :     if (poNewDimX && poNewDimY)
    8220             :     {
    8221           3 :         aosArgv.AddString("-ts");
    8222             :         aosArgv.AddString(
    8223           3 :             CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
    8224             :         aosArgv.AddString(
    8225           3 :             CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
    8226             :     }
    8227          21 :     else if (poNewDimX)
    8228             :     {
    8229           1 :         aosArgv.AddString("-ts");
    8230             :         aosArgv.AddString(
    8231           1 :             CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
    8232           1 :         aosArgv.AddString("0");
    8233             :     }
    8234          20 :     else if (poNewDimY)
    8235             :     {
    8236           1 :         aosArgv.AddString("-ts");
    8237           1 :         aosArgv.AddString("0");
    8238             :         aosArgv.AddString(
    8239           1 :             CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
    8240             :     }
    8241             : 
    8242             :     // Create a warped VRT dataset
    8243             :     GDALWarpAppOptions *psOptions =
    8244          24 :         GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
    8245          24 :     GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
    8246             :     std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
    8247          48 :         GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
    8248          24 :     GDALWarpAppOptionsFree(psOptions);
    8249          24 :     if (poReprojectedDS == nullptr)
    8250           3 :         return nullptr;
    8251             : 
    8252             :     int nBlockXSize;
    8253             :     int nBlockYSize;
    8254          21 :     poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    8255          21 :     anBlockSize.emplace_back(nBlockYSize);
    8256          21 :     anBlockSize.emplace_back(nBlockXSize);
    8257             : 
    8258          21 :     double adfGeoTransform[6] = {0, 0, 0, 0, 0, 0};
    8259          21 :     CPLErr eErr = poReprojectedDS->GetGeoTransform(adfGeoTransform);
    8260          21 :     CPLAssert(eErr == CE_None);
    8261          21 :     CPL_IGNORE_RET_VAL(eErr);
    8262             : 
    8263             :     auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
    8264           0 :         std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
    8265          42 :         poReprojectedDS->GetRasterYSize());
    8266             :     auto varY = GDALMDArrayRegularlySpaced::Create(
    8267          63 :         std::string(), poDimY->GetName(), poDimY,
    8268          84 :         adfGeoTransform[3] + adfGeoTransform[5] / 2, adfGeoTransform[5], 0);
    8269          21 :     poDimY->SetIndexingVariable(varY);
    8270             : 
    8271             :     auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
    8272           0 :         std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
    8273          42 :         poReprojectedDS->GetRasterXSize());
    8274             :     auto varX = GDALMDArrayRegularlySpaced::Create(
    8275          63 :         std::string(), poDimX->GetName(), poDimX,
    8276          84 :         adfGeoTransform[0] + adfGeoTransform[1] / 2, adfGeoTransform[1], 0);
    8277          21 :     poDimX->SetIndexingVariable(varX);
    8278             : 
    8279          21 :     apoNewDims.emplace_back(poDimY);
    8280          21 :     apoNewDims.emplace_back(poDimX);
    8281             :     auto newAr(std::shared_ptr<GDALMDArrayResampled>(
    8282          42 :         new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
    8283          21 :     newAr->SetSelf(newAr);
    8284          21 :     if (poTargetSRS)
    8285             :     {
    8286           2 :         newAr->m_poSRS.reset(poTargetSRS->Clone());
    8287             :     }
    8288             :     else
    8289             :     {
    8290          19 :         newAr->m_poSRS = poParent->GetSpatialRef();
    8291             :     }
    8292          21 :     newAr->m_poVarX = varX;
    8293          21 :     newAr->m_poVarY = varY;
    8294          21 :     newAr->m_poReprojectedDS = std::move(poReprojectedDS);
    8295          21 :     newAr->m_poParentDS = std::move(poParentDS);
    8296             : 
    8297             :     // If the input array is y,x,band ordered, the above newAr is
    8298             :     // actually band,y,x ordered as it is more convenient for
    8299             :     // GDALMDArrayResampled::IRead() implementation. But transpose that
    8300             :     // array to the order of the input array
    8301          21 :     if (bYXBandOrder)
    8302           4 :         return newAr->Transpose(std::vector<int>{1, 2, 0});
    8303             : 
    8304          19 :     return newAr;
    8305             : }
    8306             : 
    8307             : /************************************************************************/
    8308             : /*                   GDALMDArrayResampled::IRead()                      */
    8309             : /************************************************************************/
    8310             : 
    8311          29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
    8312             :                                  const size_t *count, const GInt64 *arrayStep,
    8313             :                                  const GPtrDiff_t *bufferStride,
    8314             :                                  const GDALExtendedDataType &bufferDataType,
    8315             :                                  void *pDstBuffer) const
    8316             : {
    8317          29 :     if (bufferDataType.GetClass() != GEDTC_NUMERIC)
    8318           0 :         return false;
    8319             : 
    8320             :     struct Stack
    8321             :     {
    8322             :         size_t nIters = 0;
    8323             :         GByte *dst_ptr = nullptr;
    8324             :         GPtrDiff_t dst_inc_offset = 0;
    8325             :     };
    8326             : 
    8327          29 :     const auto nDims = GetDimensionCount();
    8328          58 :     std::vector<Stack> stack(nDims + 1);  // +1 to avoid -Wnull-dereference
    8329          29 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    8330          92 :     for (size_t i = 0; i < nDims; i++)
    8331             :     {
    8332          63 :         stack[i].dst_inc_offset =
    8333          63 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    8334             :     }
    8335          29 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    8336             : 
    8337          29 :     size_t dimIdx = 0;
    8338          29 :     const size_t iDimY = nDims - 2;
    8339          29 :     const size_t iDimX = nDims - 1;
    8340             :     // Use an array to avoid a false positive warning from CLang Static
    8341             :     // Analyzer about flushCaches being never read
    8342          29 :     bool flushCaches[] = {false};
    8343             :     const bool bYXBandOrder =
    8344          29 :         m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
    8345             : 
    8346          38 : lbl_next_depth:
    8347          38 :     if (dimIdx == iDimY)
    8348             :     {
    8349          33 :         if (flushCaches[0])
    8350             :         {
    8351           5 :             flushCaches[0] = false;
    8352             :             // When changing of 2D slice, flush GDAL 2D buffers
    8353           5 :             m_poParentDS->FlushCache(false);
    8354           5 :             m_poReprojectedDS->FlushCache(false);
    8355             :         }
    8356             : 
    8357          33 :         if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
    8358             :                                     GF_Read, iDimX, iDimY, arrayStartIdx, count,
    8359             :                                     arrayStep, bufferStride, bufferDataType,
    8360          33 :                                     stack[dimIdx].dst_ptr))
    8361             :         {
    8362           0 :             return false;
    8363             :         }
    8364             :     }
    8365             :     else
    8366             :     {
    8367           5 :         stack[dimIdx].nIters = count[dimIdx];
    8368           5 :         if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
    8369           5 :             arrayStartIdx[dimIdx])
    8370             :         {
    8371           1 :             flushCaches[0] = true;
    8372             :         }
    8373           5 :         m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
    8374           5 :             arrayStartIdx[dimIdx];
    8375             :         while (true)
    8376             :         {
    8377           9 :             dimIdx++;
    8378           9 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    8379           9 :             goto lbl_next_depth;
    8380           9 :         lbl_return_to_caller:
    8381           9 :             dimIdx--;
    8382           9 :             if ((--stack[dimIdx].nIters) == 0)
    8383           5 :                 break;
    8384           4 :             flushCaches[0] = true;
    8385           4 :             ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
    8386           4 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    8387             :         }
    8388             :     }
    8389          38 :     if (dimIdx > 0)
    8390           9 :         goto lbl_return_to_caller;
    8391             : 
    8392          29 :     return true;
    8393             : }
    8394             : 
    8395             : /************************************************************************/
    8396             : /*                           GetResampled()                             */
    8397             : /************************************************************************/
    8398             : 
    8399             : /** Return an array that is a resampled / reprojected view of the current array
    8400             :  *
    8401             :  * This is the same as the C function GDALMDArrayGetResampled().
    8402             :  *
    8403             :  * Currently this method can only resample along the last 2 dimensions, unless
    8404             :  * orthorectifying a NASA EMIT dataset.
    8405             :  *
    8406             :  * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
    8407             :  * geometry lookup table (GLT) is used by default for fast orthorectification.
    8408             :  *
    8409             :  * Options available are:
    8410             :  * <ul>
    8411             :  * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
    8412             :  * Can be set to NO to use generic reprojection method.
    8413             :  * </li>
    8414             :  * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
    8415             :  * orthorectification to take into account the value of the
    8416             :  * /sensor_band_parameters/good_wavelengths array to decide if slices of the
    8417             :  * current array along the band dimension are valid.</li>
    8418             :  * </ul>
    8419             :  *
    8420             :  * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
    8421             :  *                   apoNewDims[i] can be NULL to let the method automatically
    8422             :  *                   determine it.
    8423             :  * @param resampleAlg Resampling algorithm
    8424             :  * @param poTargetSRS Target SRS, or nullptr
    8425             :  * @param papszOptions NULL-terminated list of options, or NULL.
    8426             :  *
    8427             :  * @return a new array, that holds a reference to the original one, and thus is
    8428             :  * a view of it (not a copy), or nullptr in case of error.
    8429             :  *
    8430             :  * @since 3.4
    8431             :  */
    8432          38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
    8433             :     const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
    8434             :     GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
    8435             :     CSLConstList papszOptions) const
    8436             : {
    8437          76 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    8438          38 :     if (!self)
    8439             :     {
    8440           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8441             :                  "Driver implementation issue: m_pSelf not set !");
    8442           0 :         return nullptr;
    8443             :     }
    8444          38 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    8445             :     {
    8446           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8447             :                  "GetResampled() only supports numeric data type");
    8448           0 :         return nullptr;
    8449             :     }
    8450             : 
    8451             :     // Special case for NASA EMIT datasets
    8452          76 :     auto apoDims = GetDimensions();
    8453          36 :     if (poTargetSRS == nullptr &&
    8454          59 :         ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
    8455          20 :           apoDims[1]->GetName() == "crosstrack" &&
    8456          10 :           apoDims[2]->GetName() == "bands" &&
    8457          48 :           (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
    8458           1 :            apoNewDims ==
    8459          42 :                std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
    8460          30 :                                                            apoDims[2]})) ||
    8461          51 :          (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
    8462           3 :           apoDims[1]->GetName() == "crosstrack" &&
    8463          77 :           apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
    8464          13 :         CPLTestBool(CSLFetchNameValueDef(papszOptions,
    8465             :                                          "EMIT_ORTHORECTIFICATION", "YES")))
    8466             :     {
    8467           9 :         auto poRootGroup = GetRootGroup();
    8468           9 :         if (poRootGroup)
    8469             :         {
    8470          18 :             auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
    8471          18 :             auto poLocationGroup = poRootGroup->OpenGroup("location");
    8472           9 :             if (poAttrGeotransform &&
    8473           9 :                 poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
    8474           9 :                 poAttrGeotransform->GetDimensionCount() == 1 &&
    8475          27 :                 poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
    8476           9 :                 poLocationGroup)
    8477             :             {
    8478          18 :                 auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
    8479          18 :                 auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
    8480          27 :                 if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
    8481          18 :                     poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
    8482          18 :                     poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
    8483          27 :                     poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
    8484          27 :                     poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
    8485           9 :                     poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
    8486             :                 {
    8487             :                     return CreateGLTOrthorectified(
    8488             :                         self, poRootGroup, poGLT_X, poGLT_Y,
    8489             :                         /* nGLTIndexOffset = */ -1,
    8490          18 :                         poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
    8491             :                 }
    8492             :             }
    8493             :         }
    8494             :     }
    8495             : 
    8496          29 :     if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
    8497             :                                          "EMIT_ORTHORECTIFICATION", "NO")))
    8498             :     {
    8499           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8500             :                  "EMIT_ORTHORECTIFICATION required, but dataset and/or "
    8501             :                  "parameters are not compatible with it");
    8502           0 :         return nullptr;
    8503             :     }
    8504             : 
    8505             :     return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
    8506          29 :                                         poTargetSRS, papszOptions);
    8507             : }
    8508             : 
    8509             : /************************************************************************/
    8510             : /*                         GDALDatasetFromArray()                       */
    8511             : /************************************************************************/
    8512             : 
    8513             : class GDALDatasetFromArray;
    8514             : 
    8515             : namespace
    8516             : {
    8517             : struct MetadataItem
    8518             : {
    8519             :     std::shared_ptr<GDALMDArray> poArray{};
    8520             :     std::string osName{};
    8521             :     std::string osDefinition{};
    8522             :     bool bDefinitionUsesPctForG = false;
    8523             : };
    8524             : 
    8525             : struct BandImageryMetadata
    8526             : {
    8527             :     std::shared_ptr<GDALMDArray> poCentralWavelengthArray{};
    8528             :     double dfCentralWavelengthToMicrometer = 1.0;
    8529             :     std::shared_ptr<GDALMDArray> poFWHMArray{};
    8530             :     double dfFWHMToMicrometer = 1.0;
    8531             : };
    8532             : 
    8533             : }  // namespace
    8534             : 
    8535             : class GDALRasterBandFromArray final : public GDALPamRasterBand
    8536             : {
    8537             :     std::vector<GUInt64> m_anOffset{};
    8538             :     std::vector<size_t> m_anCount{};
    8539             :     std::vector<GPtrDiff_t> m_anStride{};
    8540             : 
    8541             :   protected:
    8542             :     CPLErr IReadBlock(int, int, void *) override;
    8543             :     CPLErr IWriteBlock(int, int, void *) override;
    8544             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
    8545             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
    8546             :                      GDALDataType eBufType, GSpacing nPixelSpaceBuf,
    8547             :                      GSpacing nLineSpaceBuf,
    8548             :                      GDALRasterIOExtraArg *psExtraArg) override;
    8549             : 
    8550             :   public:
    8551             :     explicit GDALRasterBandFromArray(
    8552             :         GDALDatasetFromArray *poDSIn,
    8553             :         const std::vector<GUInt64> &anOtherDimCoord,
    8554             :         const std::vector<std::vector<MetadataItem>>
    8555             :             &aoBandParameterMetadataItems,
    8556             :         const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
    8557             :         double dfDelay, time_t nStartTime, bool &bHasWarned);
    8558             : 
    8559             :     double GetNoDataValue(int *pbHasNoData) override;
    8560             :     int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
    8561             :     uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
    8562             :     double GetOffset(int *pbHasOffset) override;
    8563             :     double GetScale(int *pbHasScale) override;
    8564             :     const char *GetUnitType() override;
    8565             :     GDALColorInterp GetColorInterpretation() override;
    8566             : };
    8567             : 
    8568             : class GDALDatasetFromArray final : public GDALPamDataset
    8569             : {
    8570             :     friend class GDALRasterBandFromArray;
    8571             : 
    8572             :     std::shared_ptr<GDALMDArray> m_poArray;
    8573             :     size_t m_iXDim;
    8574             :     size_t m_iYDim;
    8575             :     double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
    8576             :     bool m_bHasGT = false;
    8577             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
    8578             :     GDALMultiDomainMetadata m_oMDD{};
    8579             :     std::string m_osOvrFilename{};
    8580             : 
    8581             :   public:
    8582         191 :     GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
    8583             :                          size_t iXDim, size_t iYDim)
    8584         191 :         : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
    8585             :     {
    8586             :         // Initialize an overview filename from the filename of the array
    8587             :         // and its name.
    8588         191 :         const std::string &osFilename = m_poArray->GetFilename();
    8589         191 :         if (!osFilename.empty())
    8590             :         {
    8591         170 :             m_osOvrFilename = osFilename;
    8592         170 :             m_osOvrFilename += '.';
    8593        6350 :             for (char ch : m_poArray->GetName())
    8594             :             {
    8595        6180 :                 if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
    8596        5481 :                     (ch >= 'a' && ch <= 'z') || ch == '_')
    8597             :                 {
    8598        4943 :                     m_osOvrFilename += ch;
    8599             :                 }
    8600             :                 else
    8601             :                 {
    8602        1237 :                     m_osOvrFilename += '_';
    8603             :                 }
    8604             :             }
    8605         170 :             m_osOvrFilename += ".ovr";
    8606         170 :             oOvManager.Initialize(this);
    8607             :         }
    8608         191 :     }
    8609             : 
    8610             :     static GDALDatasetFromArray *
    8611             :     Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
    8612             :            size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
    8613             :            CSLConstList papszOptions);
    8614             : 
    8615         382 :     ~GDALDatasetFromArray()
    8616         191 :     {
    8617         191 :         GDALDatasetFromArray::Close();
    8618         382 :     }
    8619             : 
    8620         314 :     CPLErr Close() override
    8621             :     {
    8622         314 :         CPLErr eErr = CE_None;
    8623         314 :         if (nOpenFlags != OPEN_FLAGS_CLOSED)
    8624             :         {
    8625         314 :             if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
    8626             :                 CE_None)
    8627           0 :                 eErr = CE_Failure;
    8628         314 :             m_poArray.reset();
    8629             :         }
    8630         314 :         return eErr;
    8631             :     }
    8632             : 
    8633          49 :     CPLErr GetGeoTransform(double *padfGeoTransform) override
    8634             :     {
    8635          49 :         memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
    8636          49 :         return m_bHasGT ? CE_None : CE_Failure;
    8637             :     }
    8638             : 
    8639          57 :     const OGRSpatialReference *GetSpatialRef() const override
    8640             :     {
    8641          57 :         if (m_poArray->GetDimensionCount() < 2)
    8642           3 :             return nullptr;
    8643          54 :         m_poSRS = m_poArray->GetSpatialRef();
    8644          54 :         if (m_poSRS)
    8645             :         {
    8646          20 :             m_poSRS.reset(m_poSRS->Clone());
    8647          40 :             auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
    8648          60 :             for (auto &m : axisMapping)
    8649             :             {
    8650          40 :                 if (m == static_cast<int>(m_iXDim) + 1)
    8651          20 :                     m = 1;
    8652          20 :                 else if (m == static_cast<int>(m_iYDim) + 1)
    8653          20 :                     m = 2;
    8654             :             }
    8655          20 :             m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
    8656             :         }
    8657          54 :         return m_poSRS.get();
    8658             :     }
    8659             : 
    8660           5 :     CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
    8661             :     {
    8662           5 :         return m_oMDD.SetMetadata(papszMetadata, pszDomain);
    8663             :     }
    8664             : 
    8665         144 :     char **GetMetadata(const char *pszDomain) override
    8666             :     {
    8667         144 :         return m_oMDD.GetMetadata(pszDomain);
    8668             :     }
    8669             : 
    8670         204 :     const char *GetMetadataItem(const char *pszName,
    8671             :                                 const char *pszDomain) override
    8672             :     {
    8673         378 :         if (!m_osOvrFilename.empty() && pszName &&
    8674         390 :             EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
    8675          12 :             EQUAL(pszDomain, "OVERVIEWS"))
    8676             :         {
    8677          12 :             return m_osOvrFilename.c_str();
    8678             :         }
    8679         192 :         return m_oMDD.GetMetadataItem(pszName, pszDomain);
    8680             :     }
    8681             : };
    8682             : 
    8683             : /************************************************************************/
    8684             : /*                      GDALRasterBandFromArray()                       */
    8685             : /************************************************************************/
    8686             : 
    8687         253 : GDALRasterBandFromArray::GDALRasterBandFromArray(
    8688             :     GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
    8689             :     const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
    8690             :     const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
    8691         253 :     double dfDelay, time_t nStartTime, bool &bHasWarned)
    8692             : {
    8693         253 :     const auto &poArray(poDSIn->m_poArray);
    8694         253 :     const auto &dims(poArray->GetDimensions());
    8695         253 :     const auto nDimCount(dims.size());
    8696         506 :     const auto blockSize(poArray->GetBlockSize());
    8697         242 :     nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
    8698         495 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    8699         133 :                                                   blockSize[poDSIn->m_iYDim]))
    8700             :                       : 1;
    8701         253 :     nBlockXSize = blockSize[poDSIn->m_iXDim]
    8702         144 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    8703         144 :                                                   blockSize[poDSIn->m_iXDim]))
    8704         253 :                       : poDSIn->GetRasterXSize();
    8705         253 :     eDataType = poArray->GetDataType().GetNumericDataType();
    8706         253 :     eAccess = poDSIn->eAccess;
    8707         253 :     m_anOffset.resize(nDimCount);
    8708         253 :     m_anCount.resize(nDimCount, 1);
    8709         253 :     m_anStride.resize(nDimCount);
    8710         856 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    8711             :     {
    8712         603 :         if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
    8713             :         {
    8714         216 :             std::string dimName(dims[i]->GetName());
    8715         108 :             GUInt64 nIndex = anOtherDimCoord[j];
    8716             :             // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
    8717             :             // subsetted dimensions as generated by GetView()
    8718         108 :             if (STARTS_WITH(dimName.c_str(), "subset_"))
    8719             :             {
    8720             :                 CPLStringList aosTokens(
    8721          12 :                     CSLTokenizeString2(dimName.c_str(), "_", 0));
    8722           6 :                 if (aosTokens.size() == 5)
    8723             :                 {
    8724           6 :                     dimName = aosTokens[1];
    8725          18 :                     const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
    8726           6 :                         aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
    8727           6 :                     const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
    8728           6 :                     nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
    8729           0 :                                           : nStartDim - (nIndex * -nIncrDim);
    8730             :                 }
    8731             :             }
    8732         108 :             if (nDimCount != 3 || dimName != "Band")
    8733             :             {
    8734          52 :                 SetMetadataItem(
    8735             :                     CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
    8736             :                     CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
    8737             :             }
    8738             : 
    8739         108 :             auto indexingVar = dims[i]->GetIndexingVariable();
    8740             : 
    8741             :             // If the indexing variable is also listed in band parameter arrays,
    8742             :             // then don't use our default formatting
    8743         108 :             if (indexingVar)
    8744             :             {
    8745          38 :                 for (const auto &oItem : aoBandParameterMetadataItems[j])
    8746             :                 {
    8747          12 :                     if (oItem.poArray->GetFullName() ==
    8748          12 :                         indexingVar->GetFullName())
    8749             :                     {
    8750          12 :                         indexingVar.reset();
    8751          12 :                         break;
    8752             :                     }
    8753             :                 }
    8754             :             }
    8755             : 
    8756         134 :             if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
    8757          26 :                 indexingVar->GetDimensions()[0]->GetSize() ==
    8758          26 :                     dims[i]->GetSize())
    8759             :             {
    8760          26 :                 if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
    8761             :                 {
    8762           0 :                     if (!bHasWarned)
    8763             :                     {
    8764           0 :                         CPLError(
    8765             :                             CE_Warning, CPLE_AppDefined,
    8766             :                             "Maximum delay to load band metadata from "
    8767             :                             "dimension indexing variables has expired. "
    8768             :                             "Increase the value of the "
    8769             :                             "LOAD_EXTRA_DIM_METADATA_DELAY "
    8770             :                             "option of GDALMDArray::AsClassicDataset() "
    8771             :                             "(also accessible as the "
    8772             :                             "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
    8773             :                             "configuration option), "
    8774             :                             "or set it to 'unlimited' for unlimited delay. ");
    8775           0 :                         bHasWarned = true;
    8776             :                     }
    8777             :                 }
    8778             :                 else
    8779             :                 {
    8780          26 :                     size_t nCount = 1;
    8781          26 :                     const auto &dt(indexingVar->GetDataType());
    8782          52 :                     std::vector<GByte> abyTmp(dt.GetSize());
    8783          52 :                     if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
    8784          26 :                                           nullptr, nullptr, dt, &abyTmp[0]))
    8785             :                     {
    8786          26 :                         char *pszTmp = nullptr;
    8787          26 :                         GDALExtendedDataType::CopyValue(
    8788          26 :                             &abyTmp[0], dt, &pszTmp,
    8789          52 :                             GDALExtendedDataType::CreateString());
    8790          26 :                         if (pszTmp)
    8791             :                         {
    8792          26 :                             SetMetadataItem(
    8793             :                                 CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
    8794             :                                 pszTmp);
    8795          26 :                             CPLFree(pszTmp);
    8796             :                         }
    8797             : 
    8798          26 :                         const auto &unit(indexingVar->GetUnit());
    8799          26 :                         if (!unit.empty())
    8800             :                         {
    8801          12 :                             SetMetadataItem(
    8802             :                                 CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
    8803             :                                 unit.c_str());
    8804             :                         }
    8805             :                     }
    8806             :                 }
    8807             :             }
    8808             : 
    8809         124 :             for (const auto &oItem : aoBandParameterMetadataItems[j])
    8810             :             {
    8811          32 :                 CPLString osVal;
    8812             : 
    8813          16 :                 size_t nCount = 1;
    8814          16 :                 const auto &dt(oItem.poArray->GetDataType());
    8815          16 :                 if (oItem.bDefinitionUsesPctForG)
    8816             :                 {
    8817             :                     // There is one and only one %[x][.y]f|g in osDefinition
    8818          12 :                     std::vector<GByte> abyTmp(dt.GetSize());
    8819          12 :                     if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
    8820           6 :                                             nullptr, nullptr, dt, &abyTmp[0]))
    8821             :                     {
    8822           6 :                         double dfVal = 0;
    8823           6 :                         GDALExtendedDataType::CopyValue(
    8824           6 :                             &abyTmp[0], dt, &dfVal,
    8825          12 :                             GDALExtendedDataType::Create(GDT_Float64));
    8826           6 :                         osVal.Printf(oItem.osDefinition.c_str(), dfVal);
    8827             :                     }
    8828             :                 }
    8829             :                 else
    8830             :                 {
    8831             :                     // There should be zero or one %s in osDefinition
    8832          10 :                     char *pszValue = nullptr;
    8833          10 :                     if (dt.GetClass() == GEDTC_STRING)
    8834             :                     {
    8835           4 :                         CPL_IGNORE_RET_VAL(oItem.poArray->Read(
    8836           2 :                             &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
    8837             :                             dt, &pszValue));
    8838             :                     }
    8839             :                     else
    8840             :                     {
    8841          16 :                         std::vector<GByte> abyTmp(dt.GetSize());
    8842          16 :                         if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
    8843             :                                                 nullptr, nullptr, dt,
    8844           8 :                                                 &abyTmp[0]))
    8845             :                         {
    8846           8 :                             GDALExtendedDataType::CopyValue(
    8847           8 :                                 &abyTmp[0], dt, &pszValue,
    8848          16 :                                 GDALExtendedDataType::CreateString());
    8849             :                         }
    8850             :                     }
    8851             : 
    8852          10 :                     if (pszValue)
    8853             :                     {
    8854          10 :                         osVal.Printf(oItem.osDefinition.c_str(), pszValue);
    8855          10 :                         CPLFree(pszValue);
    8856             :                     }
    8857             :                 }
    8858          16 :                 if (!osVal.empty())
    8859          16 :                     SetMetadataItem(oItem.osName.c_str(), osVal);
    8860             :             }
    8861             : 
    8862         108 :             if (aoBandImageryMetadata[j].poCentralWavelengthArray)
    8863             :             {
    8864             :                 auto &poCentralWavelengthArray =
    8865           2 :                     aoBandImageryMetadata[j].poCentralWavelengthArray;
    8866           2 :                 size_t nCount = 1;
    8867           2 :                 const auto &dt(poCentralWavelengthArray->GetDataType());
    8868           4 :                 std::vector<GByte> abyTmp(dt.GetSize());
    8869           4 :                 if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
    8870             :                                                    &nCount, nullptr, nullptr,
    8871           2 :                                                    dt, &abyTmp[0]))
    8872             :                 {
    8873           2 :                     double dfVal = 0;
    8874           2 :                     GDALExtendedDataType::CopyValue(
    8875           2 :                         &abyTmp[0], dt, &dfVal,
    8876           4 :                         GDALExtendedDataType::Create(GDT_Float64));
    8877           2 :                     SetMetadataItem(
    8878             :                         "CENTRAL_WAVELENGTH_UM",
    8879             :                         CPLSPrintf(
    8880           2 :                             "%g", dfVal * aoBandImageryMetadata[j]
    8881           2 :                                               .dfCentralWavelengthToMicrometer),
    8882             :                         "IMAGERY");
    8883             :                 }
    8884             :             }
    8885             : 
    8886         108 :             if (aoBandImageryMetadata[j].poFWHMArray)
    8887             :             {
    8888           2 :                 auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
    8889           2 :                 size_t nCount = 1;
    8890           2 :                 const auto &dt(poFWHMArray->GetDataType());
    8891           4 :                 std::vector<GByte> abyTmp(dt.GetSize());
    8892           4 :                 if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
    8893           2 :                                       nullptr, dt, &abyTmp[0]))
    8894             :                 {
    8895           2 :                     double dfVal = 0;
    8896           2 :                     GDALExtendedDataType::CopyValue(
    8897           2 :                         &abyTmp[0], dt, &dfVal,
    8898           4 :                         GDALExtendedDataType::Create(GDT_Float64));
    8899           2 :                     SetMetadataItem(
    8900             :                         "FWHM_UM",
    8901           2 :                         CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
    8902           2 :                                                      .dfFWHMToMicrometer),
    8903             :                         "IMAGERY");
    8904             :                 }
    8905             :             }
    8906             : 
    8907         108 :             m_anOffset[i] = anOtherDimCoord[j];
    8908         108 :             j++;
    8909             :         }
    8910             :     }
    8911         253 : }
    8912             : 
    8913             : /************************************************************************/
    8914             : /*                           GetNoDataValue()                           */
    8915             : /************************************************************************/
    8916             : 
    8917          93 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
    8918             : {
    8919          93 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8920          93 :     const auto &poArray(l_poDS->m_poArray);
    8921          93 :     bool bHasNodata = false;
    8922          93 :     const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
    8923          93 :     if (pbHasNoData)
    8924          81 :         *pbHasNoData = bHasNodata;
    8925          93 :     return res;
    8926             : }
    8927             : 
    8928             : /************************************************************************/
    8929             : /*                       GetNoDataValueAsInt64()                        */
    8930             : /************************************************************************/
    8931             : 
    8932           1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
    8933             : {
    8934           1 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8935           1 :     const auto &poArray(l_poDS->m_poArray);
    8936           1 :     bool bHasNodata = false;
    8937           1 :     const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
    8938           1 :     if (pbHasNoData)
    8939           1 :         *pbHasNoData = bHasNodata;
    8940           1 :     return nodata;
    8941             : }
    8942             : 
    8943             : /************************************************************************/
    8944             : /*                      GetNoDataValueAsUInt64()                        */
    8945             : /************************************************************************/
    8946             : 
    8947           1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
    8948             : {
    8949           1 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8950           1 :     const auto &poArray(l_poDS->m_poArray);
    8951           1 :     bool bHasNodata = false;
    8952           1 :     const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
    8953           1 :     if (pbHasNoData)
    8954           1 :         *pbHasNoData = bHasNodata;
    8955           1 :     return nodata;
    8956             : }
    8957             : 
    8958             : /************************************************************************/
    8959             : /*                             GetOffset()                              */
    8960             : /************************************************************************/
    8961             : 
    8962          29 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
    8963             : {
    8964          29 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8965          29 :     const auto &poArray(l_poDS->m_poArray);
    8966          29 :     bool bHasValue = false;
    8967          29 :     double dfRes = poArray->GetOffset(&bHasValue);
    8968          29 :     if (pbHasOffset)
    8969          17 :         *pbHasOffset = bHasValue;
    8970          29 :     return dfRes;
    8971             : }
    8972             : 
    8973             : /************************************************************************/
    8974             : /*                           GetUnitType()                              */
    8975             : /************************************************************************/
    8976             : 
    8977          36 : const char *GDALRasterBandFromArray::GetUnitType()
    8978             : {
    8979          36 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8980          36 :     const auto &poArray(l_poDS->m_poArray);
    8981          36 :     return poArray->GetUnit().c_str();
    8982             : }
    8983             : 
    8984             : /************************************************************************/
    8985             : /*                             GetScale()                              */
    8986             : /************************************************************************/
    8987             : 
    8988          27 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
    8989             : {
    8990          27 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8991          27 :     const auto &poArray(l_poDS->m_poArray);
    8992          27 :     bool bHasValue = false;
    8993          27 :     double dfRes = poArray->GetScale(&bHasValue);
    8994          27 :     if (pbHasScale)
    8995          15 :         *pbHasScale = bHasValue;
    8996          27 :     return dfRes;
    8997             : }
    8998             : 
    8999             : /************************************************************************/
    9000             : /*                            IReadBlock()                              */
    9001             : /************************************************************************/
    9002             : 
    9003          68 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
    9004             :                                            void *pImage)
    9005             : {
    9006          68 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    9007          68 :     const int nXOff = nBlockXOff * nBlockXSize;
    9008          68 :     const int nYOff = nBlockYOff * nBlockYSize;
    9009          68 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    9010          68 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    9011             :     GDALRasterIOExtraArg sExtraArg;
    9012          68 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    9013         136 :     return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    9014             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    9015         136 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    9016             : }
    9017             : 
    9018             : /************************************************************************/
    9019             : /*                            IWriteBlock()                             */
    9020             : /************************************************************************/
    9021             : 
    9022           0 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
    9023             :                                             void *pImage)
    9024             : {
    9025           0 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    9026           0 :     const int nXOff = nBlockXOff * nBlockXSize;
    9027           0 :     const int nYOff = nBlockYOff * nBlockYSize;
    9028           0 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    9029           0 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    9030             :     GDALRasterIOExtraArg sExtraArg;
    9031           0 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    9032           0 :     return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    9033             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    9034           0 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    9035             : }
    9036             : 
    9037             : /************************************************************************/
    9038             : /*                            IRasterIO()                               */
    9039             : /************************************************************************/
    9040             : 
    9041         321 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
    9042             :                                           int nYOff, int nXSize, int nYSize,
    9043             :                                           void *pData, int nBufXSize,
    9044             :                                           int nBufYSize, GDALDataType eBufType,
    9045             :                                           GSpacing nPixelSpaceBuf,
    9046             :                                           GSpacing nLineSpaceBuf,
    9047             :                                           GDALRasterIOExtraArg *psExtraArg)
    9048             : {
    9049         321 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9050         321 :     const auto &poArray(l_poDS->m_poArray);
    9051         321 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
    9052         321 :     if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
    9053         321 :         (nPixelSpaceBuf % nBufferDTSize) == 0 &&
    9054         321 :         (nLineSpaceBuf % nBufferDTSize) == 0)
    9055             :     {
    9056         321 :         m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
    9057         321 :         m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
    9058         642 :         m_anStride[l_poDS->m_iXDim] =
    9059         321 :             static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
    9060         321 :         if (poArray->GetDimensionCount() >= 2)
    9061             :         {
    9062         312 :             m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
    9063         312 :             m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
    9064         312 :             m_anStride[l_poDS->m_iYDim] =
    9065         312 :                 static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
    9066             :         }
    9067         321 :         if (eRWFlag == GF_Read)
    9068             :         {
    9069         632 :             return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
    9070         316 :                                  m_anStride.data(),
    9071         632 :                                  GDALExtendedDataType::Create(eBufType), pData)
    9072         316 :                        ? CE_None
    9073         316 :                        : CE_Failure;
    9074             :         }
    9075             :         else
    9076             :         {
    9077          10 :             return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
    9078           5 :                                   m_anStride.data(),
    9079          10 :                                   GDALExtendedDataType::Create(eBufType), pData)
    9080           5 :                        ? CE_None
    9081           5 :                        : CE_Failure;
    9082             :         }
    9083             :     }
    9084           0 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    9085             :                                      pData, nBufXSize, nBufYSize, eBufType,
    9086           0 :                                      nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
    9087             : }
    9088             : 
    9089             : /************************************************************************/
    9090             : /*                      GetColorInterpretation()                        */
    9091             : /************************************************************************/
    9092             : 
    9093          45 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
    9094             : {
    9095          45 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9096          45 :     const auto &poArray(l_poDS->m_poArray);
    9097         135 :     auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
    9098          45 :     if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
    9099             :     {
    9100           6 :         bool bOK = false;
    9101           6 :         GUInt64 nStartIndex = 0;
    9102           6 :         if (poArray->GetDimensionCount() == 2 &&
    9103           0 :             poAttr->GetDimensionCount() == 0)
    9104             :         {
    9105           0 :             bOK = true;
    9106             :         }
    9107           6 :         else if (poArray->GetDimensionCount() == 3)
    9108             :         {
    9109           6 :             uint64_t nExtraDimSamples = 1;
    9110           6 :             const auto &apoDims = poArray->GetDimensions();
    9111          24 :             for (size_t i = 0; i < apoDims.size(); ++i)
    9112             :             {
    9113          18 :                 if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
    9114           6 :                     nExtraDimSamples *= apoDims[i]->GetSize();
    9115             :             }
    9116           6 :             if (poAttr->GetDimensionsSize() ==
    9117          12 :                 std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
    9118             :             {
    9119           6 :                 bOK = true;
    9120             :             }
    9121           6 :             nStartIndex = nBand - 1;
    9122             :         }
    9123           6 :         if (bOK)
    9124             :         {
    9125           6 :             const auto oStringDT = GDALExtendedDataType::CreateString();
    9126           6 :             const size_t nCount = 1;
    9127           6 :             const GInt64 arrayStep = 1;
    9128           6 :             const GPtrDiff_t bufferStride = 1;
    9129           6 :             char *pszValue = nullptr;
    9130           6 :             poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
    9131           6 :                          oStringDT, &pszValue);
    9132           6 :             if (pszValue)
    9133             :             {
    9134             :                 const auto eColorInterp =
    9135           6 :                     GDALGetColorInterpretationByName(pszValue);
    9136           6 :                 CPLFree(pszValue);
    9137           6 :                 return eColorInterp;
    9138             :             }
    9139             :         }
    9140             :     }
    9141          39 :     return GCI_Undefined;
    9142             : }
    9143             : 
    9144             : /************************************************************************/
    9145             : /*                    GDALDatasetFromArray::Create()                    */
    9146             : /************************************************************************/
    9147             : 
    9148         224 : GDALDatasetFromArray *GDALDatasetFromArray::Create(
    9149             :     const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
    9150             :     const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
    9151             : 
    9152             : {
    9153         224 :     const auto nDimCount(array->GetDimensionCount());
    9154         224 :     if (nDimCount == 0)
    9155             :     {
    9156           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    9157             :                  "Unsupported number of dimensions");
    9158           1 :         return nullptr;
    9159             :     }
    9160         445 :     if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
    9161         222 :         array->GetDataType().GetNumericDataType() == GDT_Unknown)
    9162             :     {
    9163           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    9164             :                  "Only arrays with numeric data types "
    9165             :                  "can be exposed as classic GDALDataset");
    9166           1 :         return nullptr;
    9167             :     }
    9168         222 :     if (iXDim >= nDimCount ||
    9169         208 :         (nDimCount >= 2 && (iYDim >= nDimCount || iXDim == iYDim)))
    9170             :     {
    9171           6 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
    9172           6 :         return nullptr;
    9173             :     }
    9174         216 :     GUInt64 nTotalBands = 1;
    9175         216 :     const auto &dims(array->GetDimensions());
    9176         700 :     for (size_t i = 0; i < nDimCount; ++i)
    9177             :     {
    9178         485 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9179             :         {
    9180          66 :             if (dims[i]->GetSize() > 65536 / nTotalBands)
    9181             :             {
    9182           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9183             :                          "Too many bands. Operate on a sliced view");
    9184           1 :                 return nullptr;
    9185             :             }
    9186          65 :             nTotalBands *= dims[i]->GetSize();
    9187             :         }
    9188             :     }
    9189             : 
    9190         430 :     std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
    9191         699 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    9192             :     {
    9193         484 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9194             :         {
    9195          65 :             oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
    9196          65 :             ++j;
    9197             :         }
    9198             :     }
    9199             : 
    9200         215 :     const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
    9201             : 
    9202             :     const char *pszBandMetadata =
    9203         215 :         CSLFetchNameValue(papszOptions, "BAND_METADATA");
    9204             :     std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
    9205         430 :         nNewDimCount);
    9206         215 :     if (pszBandMetadata)
    9207             :     {
    9208          21 :         if (!poRootGroup)
    9209             :         {
    9210           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9211             :                      "Root group should be provided when BAND_METADATA is set");
    9212          14 :             return nullptr;
    9213             :         }
    9214          20 :         CPLJSONDocument oDoc;
    9215          20 :         if (!oDoc.LoadMemory(pszBandMetadata))
    9216             :         {
    9217           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9218             :                      "Invalid JSON content for BAND_METADATA");
    9219           1 :             return nullptr;
    9220             :         }
    9221          19 :         auto oRoot = oDoc.GetRoot();
    9222          19 :         if (oRoot.GetType() != CPLJSONObject::Type::Array)
    9223             :         {
    9224           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9225             :                      "Value of BAND_METADATA should be an array");
    9226           1 :             return nullptr;
    9227             :         }
    9228             : 
    9229          18 :         auto oArray = oRoot.ToArray();
    9230          26 :         for (int j = 0; j < oArray.Size(); ++j)
    9231             :         {
    9232          19 :             const auto oJsonItem = oArray[j];
    9233          19 :             MetadataItem oItem;
    9234             : 
    9235          38 :             auto osBandArrayFullname = oJsonItem.GetString("array");
    9236          19 :             if (osBandArrayFullname.empty())
    9237             :             {
    9238           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9239             :                          "BAND_METADATA[%d][\"array\"] is missing", j);
    9240           1 :                 return nullptr;
    9241             :             }
    9242             :             oItem.poArray =
    9243          18 :                 poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
    9244          18 :             if (!oItem.poArray)
    9245             :             {
    9246           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9247             :                          "Array %s cannot be found",
    9248             :                          osBandArrayFullname.c_str());
    9249           1 :                 return nullptr;
    9250             :             }
    9251          17 :             if (oItem.poArray->GetDimensionCount() != 1)
    9252             :             {
    9253           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9254             :                          "Array %s is not a 1D array",
    9255             :                          osBandArrayFullname.c_str());
    9256           1 :                 return nullptr;
    9257             :             }
    9258             :             const auto &osAuxArrayDimName =
    9259          16 :                 oItem.poArray->GetDimensions()[0]->GetName();
    9260          16 :             auto oIter = oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
    9261          16 :             if (oIter == oMapArrayDimNameToExtraDimIdx.end())
    9262             :             {
    9263           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9264             :                          "Dimension %s of array %s is not a non-X/Y dimension "
    9265             :                          "of array %s",
    9266             :                          osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
    9267           1 :                          array->GetName().c_str());
    9268           1 :                 return nullptr;
    9269             :             }
    9270          15 :             const size_t iExtraDimIdx = oIter->second;
    9271          15 :             CPLAssert(iExtraDimIdx < nNewDimCount);
    9272             : 
    9273          15 :             oItem.osName = oJsonItem.GetString("item_name");
    9274          15 :             if (oItem.osName.empty())
    9275             :             {
    9276           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9277             :                          "BAND_METADATA[%d][\"item_name\"] is missing", j);
    9278           1 :                 return nullptr;
    9279             :             }
    9280             : 
    9281          28 :             const auto osDefinition = oJsonItem.GetString("item_value", "%s");
    9282             : 
    9283             :             // Check correctness of definition
    9284          14 :             bool bFirstNumericFormatter = true;
    9285          14 :             std::string osModDefinition;
    9286          14 :             bool bDefinitionUsesPctForG = false;
    9287          72 :             for (size_t k = 0; k < osDefinition.size(); ++k)
    9288             :             {
    9289          64 :                 if (osDefinition[k] == '%')
    9290             :                 {
    9291          13 :                     osModDefinition += osDefinition[k];
    9292          13 :                     if (k + 1 == osDefinition.size())
    9293             :                     {
    9294           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9295             :                                  "Value of "
    9296             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9297             :                                  "%s is invalid at offset %d",
    9298             :                                  osAuxArrayDimName.c_str(), j,
    9299             :                                  osDefinition.c_str(), int(k));
    9300           1 :                         return nullptr;
    9301             :                     }
    9302          12 :                     ++k;
    9303          12 :                     if (osDefinition[k] == '%')
    9304             :                     {
    9305           1 :                         osModDefinition += osDefinition[k];
    9306           1 :                         continue;
    9307             :                     }
    9308          11 :                     if (!bFirstNumericFormatter)
    9309             :                     {
    9310           1 :                         CPLError(
    9311             :                             CE_Failure, CPLE_AppDefined,
    9312             :                             "Value of "
    9313             :                             "BAND_METADATA[\"%s\"][%d][\"item_value\"] = %s is "
    9314             :                             "invalid at offset %d: %%[x][.y]f|g or %%s "
    9315             :                             "formatters should be specified at most once",
    9316             :                             osAuxArrayDimName.c_str(), j, osDefinition.c_str(),
    9317             :                             int(k));
    9318           1 :                         return nullptr;
    9319             :                     }
    9320          10 :                     bFirstNumericFormatter = false;
    9321          13 :                     for (; k < osDefinition.size(); ++k)
    9322             :                     {
    9323          13 :                         osModDefinition += osDefinition[k];
    9324          26 :                         if (!((osDefinition[k] >= '0' &&
    9325          12 :                                osDefinition[k] <= '9') ||
    9326          11 :                               osDefinition[k] == '.'))
    9327          10 :                             break;
    9328             :                     }
    9329          20 :                     if (k == osDefinition.size() ||
    9330          10 :                         (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
    9331           5 :                          osDefinition[k] != 's'))
    9332             :                     {
    9333           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9334             :                                  "Value of "
    9335             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9336             :                                  "%s is invalid at offset %d: only "
    9337             :                                  "%%[x][.y]f|g or %%s formatters are accepted",
    9338             :                                  osAuxArrayDimName.c_str(), j,
    9339             :                                  osDefinition.c_str(), int(k));
    9340           1 :                         return nullptr;
    9341             :                     }
    9342           9 :                     bDefinitionUsesPctForG =
    9343           9 :                         (osDefinition[k] == 'f' || osDefinition[k] == 'g');
    9344           9 :                     if (bDefinitionUsesPctForG)
    9345             :                     {
    9346           5 :                         if (oItem.poArray->GetDataType().GetClass() !=
    9347             :                             GEDTC_NUMERIC)
    9348             :                         {
    9349           1 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9350             :                                      "Data type of %s array is not numeric",
    9351             :                                      osAuxArrayDimName.c_str());
    9352           1 :                             return nullptr;
    9353             :                         }
    9354             :                     }
    9355             :                 }
    9356          56 :                 else if (osDefinition[k] == '$' &&
    9357          56 :                          k + 1 < osDefinition.size() &&
    9358           5 :                          osDefinition[k + 1] == '{')
    9359             :                 {
    9360           5 :                     const auto nPos = osDefinition.find('}', k);
    9361           5 :                     if (nPos == std::string::npos)
    9362             :                     {
    9363           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9364             :                                  "Value of "
    9365             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9366             :                                  "%s is invalid at offset %d",
    9367             :                                  osAuxArrayDimName.c_str(), j,
    9368             :                                  osDefinition.c_str(), int(k));
    9369           2 :                         return nullptr;
    9370             :                     }
    9371             :                     const auto osAttrName =
    9372           4 :                         osDefinition.substr(k + 2, nPos - (k + 2));
    9373           4 :                     auto poAttr = oItem.poArray->GetAttribute(osAttrName);
    9374           4 :                     if (!poAttr)
    9375             :                     {
    9376           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9377             :                                  "Value of "
    9378             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9379             :                                  "%s is invalid: %s is not an attribute of %s",
    9380             :                                  osAuxArrayDimName.c_str(), j,
    9381             :                                  osDefinition.c_str(), osAttrName.c_str(),
    9382             :                                  osAuxArrayDimName.c_str());
    9383           1 :                         return nullptr;
    9384             :                     }
    9385           3 :                     k = nPos;
    9386           3 :                     const char *pszValue = poAttr->ReadAsString();
    9387           3 :                     if (!pszValue)
    9388             :                     {
    9389           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9390             :                                  "Cannot get value of attribute %s of %s as a "
    9391             :                                  "string",
    9392             :                                  osAttrName.c_str(), osAuxArrayDimName.c_str());
    9393           0 :                         return nullptr;
    9394             :                     }
    9395           3 :                     osModDefinition += pszValue;
    9396             :                 }
    9397             :                 else
    9398             :                 {
    9399          46 :                     osModDefinition += osDefinition[k];
    9400             :                 }
    9401             :             }
    9402             : 
    9403           8 :             oItem.osDefinition = std::move(osModDefinition);
    9404           8 :             oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
    9405             : 
    9406           8 :             aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
    9407           8 :                 std::move(oItem));
    9408             :         }
    9409             :     }
    9410             : 
    9411         402 :     std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
    9412             :     const char *pszBandImageryMetadata =
    9413         201 :         CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
    9414         201 :     if (pszBandImageryMetadata)
    9415             :     {
    9416          12 :         if (!poRootGroup)
    9417             :         {
    9418           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9419             :                      "Root group should be provided when BAND_IMAGERY_METADATA "
    9420             :                      "is set");
    9421          10 :             return nullptr;
    9422             :         }
    9423          11 :         CPLJSONDocument oDoc;
    9424          11 :         if (!oDoc.LoadMemory(pszBandImageryMetadata))
    9425             :         {
    9426           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9427             :                      "Invalid JSON content for BAND_IMAGERY_METADATA");
    9428           1 :             return nullptr;
    9429             :         }
    9430          10 :         auto oRoot = oDoc.GetRoot();
    9431          10 :         if (oRoot.GetType() != CPLJSONObject::Type::Object)
    9432             :         {
    9433           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9434             :                      "Value of BAND_IMAGERY_METADATA should be an object");
    9435           1 :             return nullptr;
    9436             :         }
    9437          12 :         for (const auto &oJsonItem : oRoot.GetChildren())
    9438             :         {
    9439          22 :             if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
    9440          12 :                 oJsonItem.GetName() == "FWHM_UM")
    9441             :             {
    9442          18 :                 auto osBandArrayFullname = oJsonItem.GetString("array");
    9443           9 :                 if (osBandArrayFullname.empty())
    9444             :                 {
    9445           1 :                     CPLError(
    9446             :                         CE_Failure, CPLE_AppDefined,
    9447             :                         "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] is missing",
    9448           2 :                         oJsonItem.GetName().c_str());
    9449           1 :                     return nullptr;
    9450             :                 }
    9451             :                 auto poArray =
    9452           8 :                     poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
    9453           8 :                 if (!poArray)
    9454             :                 {
    9455           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9456             :                              "Array %s cannot be found",
    9457             :                              osBandArrayFullname.c_str());
    9458           1 :                     return nullptr;
    9459             :                 }
    9460           7 :                 if (poArray->GetDimensionCount() != 1)
    9461             :                 {
    9462           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9463             :                              "Array %s is not a 1D array",
    9464             :                              osBandArrayFullname.c_str());
    9465           1 :                     return nullptr;
    9466             :                 }
    9467             :                 const auto &osAuxArrayDimName =
    9468           6 :                     poArray->GetDimensions()[0]->GetName();
    9469             :                 auto oIter =
    9470           6 :                     oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
    9471           6 :                 if (oIter == oMapArrayDimNameToExtraDimIdx.end())
    9472             :                 {
    9473           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9474             :                              "Dimension \"%s\" of array \"%s\" is not a "
    9475             :                              "non-X/Y dimension of array \"%s\"",
    9476             :                              osAuxArrayDimName.c_str(),
    9477             :                              osBandArrayFullname.c_str(),
    9478           1 :                              array->GetName().c_str());
    9479           1 :                     return nullptr;
    9480             :                 }
    9481           5 :                 const size_t iExtraDimIdx = oIter->second;
    9482           5 :                 CPLAssert(iExtraDimIdx < nNewDimCount);
    9483             : 
    9484          10 :                 std::string osUnit = oJsonItem.GetString("unit", "um");
    9485           5 :                 if (STARTS_WITH(osUnit.c_str(), "${"))
    9486             :                 {
    9487           3 :                     if (osUnit.back() != '}')
    9488             :                     {
    9489           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9490             :                                  "Value of "
    9491             :                                  "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
    9492             :                                  "%s is invalid",
    9493           2 :                                  oJsonItem.GetName().c_str(), osUnit.c_str());
    9494           2 :                         return nullptr;
    9495             :                     }
    9496           2 :                     const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
    9497           2 :                     auto poAttr = poArray->GetAttribute(osAttrName);
    9498           2 :                     if (!poAttr)
    9499             :                     {
    9500           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9501             :                                  "Value of "
    9502             :                                  "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
    9503             :                                  "%s is invalid: %s is not an attribute of %s",
    9504           2 :                                  oJsonItem.GetName().c_str(), osUnit.c_str(),
    9505             :                                  osAttrName.c_str(),
    9506             :                                  osBandArrayFullname.c_str());
    9507           1 :                         return nullptr;
    9508             :                     }
    9509           1 :                     const char *pszValue = poAttr->ReadAsString();
    9510           1 :                     if (!pszValue)
    9511             :                     {
    9512           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9513             :                                  "Cannot get value of attribute %s of %s as a "
    9514             :                                  "string",
    9515             :                                  osAttrName.c_str(),
    9516             :                                  osBandArrayFullname.c_str());
    9517           0 :                         return nullptr;
    9518             :                     }
    9519           1 :                     osUnit = pszValue;
    9520             :                 }
    9521           3 :                 double dfConvToUM = 1.0;
    9522           7 :                 if (osUnit == "nm" || osUnit == "nanometre" ||
    9523           9 :                     osUnit == "nanometres" || osUnit == "nanometer" ||
    9524           2 :                     osUnit == "nanometers")
    9525             :                 {
    9526           1 :                     dfConvToUM = 1e-3;
    9527             :                 }
    9528           3 :                 else if (!(osUnit == "um" || osUnit == "micrometre" ||
    9529           1 :                            osUnit == "micrometres" || osUnit == "micrometer" ||
    9530           1 :                            osUnit == "micrometers"))
    9531             :                 {
    9532           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9533             :                              "Unhandled value for "
    9534             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
    9535           2 :                              oJsonItem.GetName().c_str(), osUnit.c_str());
    9536           1 :                     return nullptr;
    9537             :                 }
    9538             : 
    9539           2 :                 BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
    9540           2 :                 if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
    9541             :                 {
    9542           1 :                     item.poCentralWavelengthArray = std::move(poArray);
    9543           1 :                     item.dfCentralWavelengthToMicrometer = dfConvToUM;
    9544             :                 }
    9545             :                 else
    9546             :                 {
    9547           1 :                     item.poFWHMArray = std::move(poArray);
    9548           1 :                     item.dfFWHMToMicrometer = dfConvToUM;
    9549             :                 }
    9550             :             }
    9551             :             else
    9552             :             {
    9553           1 :                 CPLError(CE_Warning, CPLE_AppDefined,
    9554             :                          "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
    9555           2 :                          oJsonItem.GetName().c_str());
    9556             :             }
    9557             :         }
    9558             :     }
    9559             : 
    9560         382 :     auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
    9561             : 
    9562         191 :     poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
    9563             : 
    9564         191 :     poDS->nRasterYSize =
    9565         191 :         nDimCount < 2 ? 1
    9566         180 :                       : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    9567         180 :                                                   dims[iYDim]->GetSize()));
    9568         382 :     poDS->nRasterXSize = static_cast<int>(
    9569         191 :         std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
    9570             : 
    9571         382 :     std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
    9572         382 :     std::vector<GUInt64> anStackIters(nDimCount);
    9573         382 :     std::vector<size_t> anMapNewToOld(nNewDimCount);
    9574         603 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    9575             :     {
    9576         412 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9577             :         {
    9578          41 :             anMapNewToOld[j] = i;
    9579          41 :             j++;
    9580             :         }
    9581             :     }
    9582             : 
    9583         382 :     poDS->m_bHasGT =
    9584         191 :         array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_adfGeoTransform);
    9585             : 
    9586         382 :     const auto attrs(array->GetAttributes());
    9587         270 :     for (const auto &attr : attrs)
    9588             :     {
    9589          79 :         if (attr->GetName() != "COLOR_INTERPRETATION")
    9590             :         {
    9591         144 :             auto stringArray = attr->ReadAsStringArray();
    9592         144 :             std::string val;
    9593          72 :             if (stringArray.size() > 1)
    9594             :             {
    9595          22 :                 val += '{';
    9596             :             }
    9597         166 :             for (int i = 0; i < stringArray.size(); ++i)
    9598             :             {
    9599          94 :                 if (i > 0)
    9600          22 :                     val += ',';
    9601          94 :                 val += stringArray[i];
    9602             :             }
    9603          72 :             if (stringArray.size() > 1)
    9604             :             {
    9605          22 :                 val += '}';
    9606             :             }
    9607          72 :             poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
    9608             :         }
    9609             :     }
    9610             : 
    9611         191 :     const char *pszDelay = CSLFetchNameValueDef(
    9612             :         papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
    9613             :         CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
    9614             :     const double dfDelay =
    9615         191 :         EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
    9616         191 :     const auto nStartTime = time(nullptr);
    9617         191 :     bool bHasWarned = false;
    9618             :     // Instantiate bands by iterating over non-XY variables
    9619         191 :     size_t iDim = 0;
    9620         191 :     int nCurBand = 1;
    9621         296 : lbl_next_depth:
    9622         296 :     if (iDim < nNewDimCount)
    9623             :     {
    9624          43 :         anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
    9625          43 :         anOtherDimCoord[iDim] = 0;
    9626             :         while (true)
    9627             :         {
    9628         105 :             ++iDim;
    9629         105 :             goto lbl_next_depth;
    9630         105 :         lbl_return_to_caller:
    9631         105 :             --iDim;
    9632         105 :             --anStackIters[iDim];
    9633         105 :             if (anStackIters[iDim] == 0)
    9634          43 :                 break;
    9635          62 :             ++anOtherDimCoord[iDim];
    9636             :         }
    9637             :     }
    9638             :     else
    9639             :     {
    9640         506 :         poDS->SetBand(nCurBand,
    9641             :                       new GDALRasterBandFromArray(
    9642         253 :                           poDS.get(), anOtherDimCoord,
    9643             :                           aoBandParameterMetadataItems, aoBandImageryMetadata,
    9644         253 :                           dfDelay, nStartTime, bHasWarned));
    9645         253 :         ++nCurBand;
    9646             :     }
    9647         296 :     if (iDim > 0)
    9648         105 :         goto lbl_return_to_caller;
    9649             : 
    9650         191 :     if (!array->GetFilename().empty())
    9651             :     {
    9652         170 :         poDS->SetPhysicalFilename(array->GetFilename().c_str());
    9653             :         std::string osDerivedDatasetName(
    9654             :             CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
    9655         340 :                        int(iYDim), array->GetFullName().c_str()));
    9656         170 :         if (!array->GetContext().empty())
    9657             :         {
    9658           2 :             osDerivedDatasetName += " with context ";
    9659           2 :             osDerivedDatasetName += array->GetContext();
    9660             :         }
    9661         170 :         poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
    9662         170 :         poDS->TryLoadXML();
    9663             : 
    9664           2 :         for (const auto &[pszKey, pszValue] :
    9665             :              cpl::IterateNameValue(static_cast<CSLConstList>(
    9666         172 :                  poDS->GDALPamDataset::GetMetadata())))
    9667             :         {
    9668           1 :             poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
    9669             :         }
    9670             :     }
    9671             : 
    9672         191 :     return poDS.release();
    9673             : }
    9674             : 
    9675             : /************************************************************************/
    9676             : /*                          AsClassicDataset()                         */
    9677             : /************************************************************************/
    9678             : 
    9679             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
    9680             :  *
    9681             :  * In the case of > 2D arrays, additional dimensions will be represented as
    9682             :  * raster bands.
    9683             :  *
    9684             :  * The "reverse" method is GDALRasterBand::AsMDArray().
    9685             :  *
    9686             :  * This is the same as the C function GDALMDArrayAsClassicDataset().
    9687             :  *
    9688             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
    9689             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
    9690             :  *              Ignored if the dimension count is 1.
    9691             :  * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
    9692             :  *                    and BAND_IMAGERY_METADATA option.
    9693             :  * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
    9694             :  *                     nullptr. Current supported options are:
    9695             :  *                     <ul>
    9696             :  *                     <li>BAND_METADATA: JSON serialized array defining which
    9697             :  *                         arrays of the poRootGroup, indexed by non-X and Y
    9698             :  *                         dimensions, should be mapped as band metadata items.
    9699             :  *                         Each array item should be an object with the
    9700             :  *                         following members:
    9701             :  *                         - "array": full name of a band parameter array.
    9702             :  *                           Such array must be a one
    9703             :  *                           dimensional array, and its dimension must be one of
    9704             :  *                           the dimensions of the array on which the method is
    9705             :  *                           called (excluding the X and Y dimensons).
    9706             :  *                         - "item_name": band metadata item name
    9707             :  *                         - "item_value": (optional) String, where "%[x][.y]f",
    9708             :  *                           "%[x][.y]g" or "%s" printf-like formatting can be
    9709             :  *                           used to format the corresponding value of the
    9710             :  *                           parameter array. The percentage character should be
    9711             :  *                           repeated: "%%"
    9712             :  *                           "${attribute_name}" can also be used to include the
    9713             :  *                           value of an attribute for the array.
    9714             :  *                           If "item_value" is not provided, a default formatting
    9715             :  *                           of the value will be applied.
    9716             :  *
    9717             :  *                         Example:
    9718             :  *                         [
    9719             :  *                            {
    9720             :  *                              "array": "/sensor_band_parameters/wavelengths",
    9721             :  *                              "item_name": "WAVELENGTH",
    9722             :  *                              "item_value": "%.1f ${units}"
    9723             :  *                            },
    9724             :  *                            {
    9725             :  *                              "array": "/sensor_band_parameters/fwhm",
    9726             :  *                              "item_name": "FWHM"
    9727             :  *                            },
    9728             :  *                            {
    9729             :  *                              "array": "/sensor_band_parameters/fwhm",
    9730             :  *                              "item_name": "FWHM_UNIT",
    9731             :  *                              "item_value": "${units}"
    9732             :  *                            }
    9733             :  *                         ]
    9734             :  *                     </li>
    9735             :  *                     <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
    9736             :  *                         JSON serialized object defining which arrays of the
    9737             :  *                         poRootGroup, indexed by non-X and Y dimensions,
    9738             :  *                         should be mapped as band metadata items in the
    9739             :  *                         band IMAGERY domain.
    9740             :  *                         The object currently accepts 2 members:
    9741             :  *                         - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
    9742             :  *                           micrometers.
    9743             :  *                         - "FWHM_UM": Full-width half-maximum
    9744             :  *                           in micrometers.
    9745             :  *                         The value of each member should be an object with the
    9746             :  *                         following members:
    9747             :  *                         - "array": (required) full name of a band parameter
    9748             :  *                           array.
    9749             :  *                           Such array must be a one dimensional array, and its
    9750             :  *                           dimension must be one of the dimensions of the
    9751             :  *                           array on which the method is called
    9752             :  *                           (excluding the X and Y dimensons).
    9753             :  *                         - "unit": (optional) unit of the values pointed in
    9754             :  *                           the above array.
    9755             :  *                           Can be a literal string or a string of the form
    9756             :  *                           "${attribute_name}" to point to an attribute for
    9757             :  *                           the array.
    9758             :  *                           Accepted values are "um", "micrometer"
    9759             :  *                           (with UK vs US spelling, singular or plural), "nm",
    9760             :  *                           "nanometer" (with UK vs US spelling, singular or
    9761             :  *                           plural)
    9762             :  *                           If not provided, micrometer is assumed.
    9763             :  *
    9764             :  *                         Example for EMIT datasets:
    9765             :  *                         {
    9766             :  *                            "CENTRAL_WAVELENGTH_UM": {
    9767             :  *                                "array": "/sensor_band_parameters/wavelengths",
    9768             :  *                                "unit": "${units}"
    9769             :  *                            },
    9770             :  *                            "FWHM_UM": {
    9771             :  *                                "array": "/sensor_band_parameters/fwhm",
    9772             :  *                                "unit": "${units}"
    9773             :  *                            }
    9774             :  *                         }
    9775             :  *                     </li>
    9776             :  *                     <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
    9777             :  *                         seconds allowed to set the DIM_{dimname}_VALUE band
    9778             :  *                         metadata items from the indexing variable of the
    9779             :  *                         dimensions.
    9780             :  *                         Default value is 5. 'unlimited' can be used to mean
    9781             :  *                         unlimited delay. Can also be defined globally with
    9782             :  *                         the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
    9783             :  *                         option.</li>
    9784             :  *                     </ul>
    9785             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
    9786             :  */
    9787             : GDALDataset *
    9788         224 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
    9789             :                               const std::shared_ptr<GDALGroup> &poRootGroup,
    9790             :                               CSLConstList papszOptions) const
    9791             : {
    9792         448 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    9793         224 :     if (!self)
    9794             :     {
    9795           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    9796             :                  "Driver implementation issue: m_pSelf not set !");
    9797           0 :         return nullptr;
    9798             :     }
    9799         224 :     return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
    9800         224 :                                         papszOptions);
    9801             : }
    9802             : 
    9803             : /************************************************************************/
    9804             : /*                           GetStatistics()                            */
    9805             : /************************************************************************/
    9806             : 
    9807             : /**
    9808             :  * \brief Fetch statistics.
    9809             :  *
    9810             :  * Returns the minimum, maximum, mean and standard deviation of all
    9811             :  * pixel values in this array.
    9812             :  *
    9813             :  * If bForce is FALSE results will only be returned if it can be done
    9814             :  * quickly (i.e. without scanning the data).  If bForce is FALSE and
    9815             :  * results cannot be returned efficiently, the method will return CE_Warning
    9816             :  * but no warning will have been issued.   This is a non-standard use of
    9817             :  * the CE_Warning return value to indicate "nothing done".
    9818             :  *
    9819             :  * When cached statistics are not available, and bForce is TRUE,
    9820             :  * ComputeStatistics() is called.
    9821             :  *
    9822             :  * Note that file formats using PAM (Persistent Auxiliary Metadata) services
    9823             :  * will generally cache statistics in the .aux.xml file allowing fast fetch
    9824             :  * after the first request.
    9825             :  *
    9826             :  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
    9827             :  *
    9828             :  * This method is the same as the C function GDALMDArrayGetStatistics().
    9829             :  *
    9830             :  * @param bApproxOK Currently ignored. In the future, should be set to true
    9831             :  * if statistics on the whole array are wished, or to false if a subset of it
    9832             :  * may be used.
    9833             :  *
    9834             :  * @param bForce If false statistics will only be returned if it can
    9835             :  * be done without rescanning the image.
    9836             :  *
    9837             :  * @param pdfMin Location into which to load image minimum (may be NULL).
    9838             :  *
    9839             :  * @param pdfMax Location into which to load image maximum (may be NULL).-
    9840             :  *
    9841             :  * @param pdfMean Location into which to load image mean (may be NULL).
    9842             :  *
    9843             :  * @param pdfStdDev Location into which to load image standard deviation
    9844             :  * (may be NULL).
    9845             :  *
    9846             :  * @param pnValidCount Number of samples whose value is different from the
    9847             :  * nodata value. (may be NULL)
    9848             :  *
    9849             :  * @param pfnProgress a function to call to report progress, or NULL.
    9850             :  *
    9851             :  * @param pProgressData application data to pass to the progress function.
    9852             :  *
    9853             :  * @return CE_None on success, CE_Warning if no values returned,
    9854             :  * CE_Failure if an error occurs.
    9855             :  *
    9856             :  * @since GDAL 3.2
    9857             :  */
    9858             : 
    9859           7 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
    9860             :                                   double *pdfMax, double *pdfMean,
    9861             :                                   double *pdfStdDev, GUInt64 *pnValidCount,
    9862             :                                   GDALProgressFunc pfnProgress,
    9863             :                                   void *pProgressData)
    9864             : {
    9865           7 :     if (!bForce)
    9866           1 :         return CE_Warning;
    9867             : 
    9868          12 :     return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
    9869           6 :                              pnValidCount, pfnProgress, pProgressData, nullptr)
    9870           6 :                ? CE_None
    9871           6 :                : CE_Failure;
    9872             : }
    9873             : 
    9874             : /************************************************************************/
    9875             : /*                         ComputeStatistics()                          */
    9876             : /************************************************************************/
    9877             : 
    9878             : /**
    9879             :  * \brief Compute statistics.
    9880             :  *
    9881             :  * Returns the minimum, maximum, mean and standard deviation of all
    9882             :  * pixel values in this array.
    9883             :  *
    9884             :  * Pixels taken into account in statistics are those whose mask value
    9885             :  * (as determined by GetMask()) is non-zero.
    9886             :  *
    9887             :  * Once computed, the statistics will generally be "set" back on the
    9888             :  * owing dataset.
    9889             :  *
    9890             :  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
    9891             :  *
    9892             :  * This method is the same as the C functions GDALMDArrayComputeStatistics().
    9893             :  * and GDALMDArrayComputeStatisticsEx().
    9894             :  *
    9895             :  * @param bApproxOK Currently ignored. In the future, should be set to true
    9896             :  * if statistics on the whole array are wished, or to false if a subset of it
    9897             :  * may be used.
    9898             :  *
    9899             :  * @param pdfMin Location into which to load image minimum (may be NULL).
    9900             :  *
    9901             :  * @param pdfMax Location into which to load image maximum (may be NULL).-
    9902             :  *
    9903             :  * @param pdfMean Location into which to load image mean (may be NULL).
    9904             :  *
    9905             :  * @param pdfStdDev Location into which to load image standard deviation
    9906             :  * (may be NULL).
    9907             :  *
    9908             :  * @param pnValidCount Number of samples whose value is different from the
    9909             :  * nodata value. (may be NULL)
    9910             :  *
    9911             :  * @param pfnProgress a function to call to report progress, or NULL.
    9912             :  *
    9913             :  * @param pProgressData application data to pass to the progress function.
    9914             :  *
    9915             :  * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
    9916             :  *                     Options are driver specific. For now the netCDF and Zarr
    9917             :  *                     drivers recognize UPDATE_METADATA=YES, whose effect is
    9918             :  *                     to add or update the actual_range attribute with the
    9919             :  *                     computed min/max, only if done on the full array, in non
    9920             :  *                     approximate mode, and the dataset is opened in update
    9921             :  *                     mode.
    9922             :  *
    9923             :  * @return true on success
    9924             :  *
    9925             :  * @since GDAL 3.2
    9926             :  */
    9927             : 
    9928          10 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
    9929             :                                     double *pdfMax, double *pdfMean,
    9930             :                                     double *pdfStdDev, GUInt64 *pnValidCount,
    9931             :                                     GDALProgressFunc pfnProgress,
    9932             :                                     void *pProgressData,
    9933             :                                     CSLConstList papszOptions)
    9934             : {
    9935             :     struct StatsPerChunkType
    9936             :     {
    9937             :         const GDALMDArray *array = nullptr;
    9938             :         std::shared_ptr<GDALMDArray> poMask{};
    9939             :         double dfMin = cpl::NumericLimits<double>::max();
    9940             :         double dfMax = -cpl::NumericLimits<double>::max();
    9941             :         double dfMean = 0.0;
    9942             :         double dfM2 = 0.0;
    9943             :         GUInt64 nValidCount = 0;
    9944             :         std::vector<GByte> abyData{};
    9945             :         std::vector<double> adfData{};
    9946             :         std::vector<GByte> abyMaskData{};
    9947             :         GDALProgressFunc pfnProgress = nullptr;
    9948             :         void *pProgressData = nullptr;
    9949             :     };
    9950             : 
    9951          10 :     const auto PerChunkFunc = [](GDALAbstractMDArray *,
    9952             :                                  const GUInt64 *chunkArrayStartIdx,
    9953             :                                  const size_t *chunkCount, GUInt64 iCurChunk,
    9954             :                                  GUInt64 nChunkCount, void *pUserData)
    9955             :     {
    9956          10 :         StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
    9957          10 :         const GDALMDArray *array = data->array;
    9958          10 :         const GDALMDArray *poMask = data->poMask.get();
    9959          10 :         const size_t nDims = array->GetDimensionCount();
    9960          10 :         size_t nVals = 1;
    9961          27 :         for (size_t i = 0; i < nDims; i++)
    9962          17 :             nVals *= chunkCount[i];
    9963             : 
    9964             :         // Get mask
    9965          10 :         data->abyMaskData.resize(nVals);
    9966          10 :         if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
    9967          10 :                            poMask->GetDataType(), &data->abyMaskData[0])))
    9968             :         {
    9969           0 :             return false;
    9970             :         }
    9971             : 
    9972             :         // Get data
    9973          10 :         const auto &oType = array->GetDataType();
    9974          10 :         if (oType.GetNumericDataType() == GDT_Float64)
    9975             :         {
    9976           4 :             data->adfData.resize(nVals);
    9977           4 :             if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
    9978           4 :                              oType, &data->adfData[0]))
    9979             :             {
    9980           0 :                 return false;
    9981             :             }
    9982             :         }
    9983             :         else
    9984             :         {
    9985           6 :             data->abyData.resize(nVals * oType.GetSize());
    9986           6 :             if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
    9987           6 :                              oType, &data->abyData[0]))
    9988             :             {
    9989           0 :                 return false;
    9990             :             }
    9991           6 :             data->adfData.resize(nVals);
    9992           6 :             GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
    9993           6 :                             static_cast<int>(oType.GetSize()),
    9994           6 :                             &data->adfData[0], GDT_Float64,
    9995             :                             static_cast<int>(sizeof(double)),
    9996             :                             static_cast<GPtrDiff_t>(nVals));
    9997             :         }
    9998         469 :         for (size_t i = 0; i < nVals; i++)
    9999             :         {
   10000         459 :             if (data->abyMaskData[i])
   10001             :             {
   10002         454 :                 const double dfValue = data->adfData[i];
   10003         454 :                 data->dfMin = std::min(data->dfMin, dfValue);
   10004         454 :                 data->dfMax = std::max(data->dfMax, dfValue);
   10005         454 :                 data->nValidCount++;
   10006         454 :                 const double dfDelta = dfValue - data->dfMean;
   10007         454 :                 data->dfMean += dfDelta / data->nValidCount;
   10008         454 :                 data->dfM2 += dfDelta * (dfValue - data->dfMean);
   10009             :             }
   10010             :         }
   10011          10 :         if (data->pfnProgress &&
   10012           0 :             !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
   10013             :                                "", data->pProgressData))
   10014             :         {
   10015           0 :             return false;
   10016             :         }
   10017          10 :         return true;
   10018             :     };
   10019             : 
   10020          10 :     const auto &oType = GetDataType();
   10021          20 :     if (oType.GetClass() != GEDTC_NUMERIC ||
   10022          10 :         GDALDataTypeIsComplex(oType.GetNumericDataType()))
   10023             :     {
   10024           0 :         CPLError(
   10025             :             CE_Failure, CPLE_NotSupported,
   10026             :             "Statistics can only be computed on non-complex numeric data type");
   10027           0 :         return false;
   10028             :     }
   10029             : 
   10030          10 :     const size_t nDims = GetDimensionCount();
   10031          20 :     std::vector<GUInt64> arrayStartIdx(nDims);
   10032          20 :     std::vector<GUInt64> count(nDims);
   10033          10 :     const auto &poDims = GetDimensions();
   10034          27 :     for (size_t i = 0; i < nDims; i++)
   10035             :     {
   10036          17 :         count[i] = poDims[i]->GetSize();
   10037             :     }
   10038          10 :     const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
   10039             :     const size_t nMaxChunkSize =
   10040             :         pszSwathSize
   10041          10 :             ? static_cast<size_t>(
   10042           0 :                   std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
   10043           0 :                            CPLAtoGIntBig(pszSwathSize)))
   10044             :             : static_cast<size_t>(
   10045          10 :                   std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
   10046          10 :                            GDALGetCacheMax64() / 4));
   10047          20 :     StatsPerChunkType sData;
   10048          10 :     sData.array = this;
   10049          10 :     sData.poMask = GetMask(nullptr);
   10050          10 :     if (sData.poMask == nullptr)
   10051             :     {
   10052           0 :         return false;
   10053             :     }
   10054          10 :     sData.pfnProgress = pfnProgress;
   10055          10 :     sData.pProgressData = pProgressData;
   10056          10 :     if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
   10057          20 :                          GetProcessingChunkSize(nMaxChunkSize).data(),
   10058          10 :                          PerChunkFunc, &sData))
   10059             :     {
   10060           0 :         return false;
   10061             :     }
   10062             : 
   10063          10 :     if (pdfMin)
   10064          10 :         *pdfMin = sData.dfMin;
   10065             : 
   10066          10 :     if (pdfMax)
   10067          10 :         *pdfMax = sData.dfMax;
   10068             : 
   10069          10 :     if (pdfMean)
   10070           8 :         *pdfMean = sData.dfMean;
   10071             : 
   10072             :     const double dfStdDev =
   10073          10 :         sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
   10074          10 :     if (pdfStdDev)
   10075           8 :         *pdfStdDev = dfStdDev;
   10076             : 
   10077          10 :     if (pnValidCount)
   10078           8 :         *pnValidCount = sData.nValidCount;
   10079             : 
   10080          10 :     SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
   10081          10 :                   sData.nValidCount, papszOptions);
   10082             : 
   10083          10 :     return true;
   10084             : }
   10085             : 
   10086             : /************************************************************************/
   10087             : /*                            SetStatistics()                           */
   10088             : /************************************************************************/
   10089             : //! @cond Doxygen_Suppress
   10090           5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
   10091             :                                 double /* dfMax */, double /* dfMean */,
   10092             :                                 double /* dfStdDev */,
   10093             :                                 GUInt64 /* nValidCount */,
   10094             :                                 CSLConstList /* papszOptions */)
   10095             : {
   10096           5 :     CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
   10097           5 :     return false;
   10098             : }
   10099             : 
   10100             : //! @endcond
   10101             : 
   10102             : /************************************************************************/
   10103             : /*                           ClearStatistics()                          */
   10104             : /************************************************************************/
   10105             : 
   10106             : /**
   10107             :  * \brief Clear statistics.
   10108             :  *
   10109             :  * @since GDAL 3.4
   10110             :  */
   10111           0 : void GDALMDArray::ClearStatistics()
   10112             : {
   10113           0 : }
   10114             : 
   10115             : /************************************************************************/
   10116             : /*                      GetCoordinateVariables()                        */
   10117             : /************************************************************************/
   10118             : 
   10119             : /**
   10120             :  * \brief Return coordinate variables.
   10121             :  *
   10122             :  * Coordinate variables are an alternate way of indexing an array that can
   10123             :  * be sometimes used. For example, an array collected through remote sensing
   10124             :  * might be indexed by (scanline, pixel). But there can be
   10125             :  * a longitude and latitude arrays alongside that are also both indexed by
   10126             :  * (scanline, pixel), and are referenced from operational arrays for
   10127             :  * reprojection purposes.
   10128             :  *
   10129             :  * For netCDF, this will return the arrays referenced by the "coordinates"
   10130             :  * attribute.
   10131             :  *
   10132             :  * This method is the same as the C function
   10133             :  * GDALMDArrayGetCoordinateVariables().
   10134             :  *
   10135             :  * @return a vector of arrays
   10136             :  *
   10137             :  * @since GDAL 3.4
   10138             :  */
   10139             : 
   10140             : std::vector<std::shared_ptr<GDALMDArray>>
   10141          13 : GDALMDArray::GetCoordinateVariables() const
   10142             : {
   10143          13 :     return {};
   10144             : }
   10145             : 
   10146             : /************************************************************************/
   10147             : /*                       ~GDALExtendedDataType()                        */
   10148             : /************************************************************************/
   10149             : 
   10150             : GDALExtendedDataType::~GDALExtendedDataType() = default;
   10151             : 
   10152             : /************************************************************************/
   10153             : /*                        GDALExtendedDataType()                        */
   10154             : /************************************************************************/
   10155             : 
   10156        8328 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
   10157        8328 :                                            GDALExtendedDataTypeSubType eSubType)
   10158             :     : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
   10159        8328 :       m_nMaxStringLength(nMaxStringLength)
   10160             : {
   10161        8328 : }
   10162             : 
   10163             : /************************************************************************/
   10164             : /*                        GDALExtendedDataType()                        */
   10165             : /************************************************************************/
   10166             : 
   10167       35854 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
   10168             :     : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
   10169       35854 :       m_nSize(GDALGetDataTypeSizeBytes(eType))
   10170             : {
   10171       35854 : }
   10172             : 
   10173             : /************************************************************************/
   10174             : /*                        GDALExtendedDataType()                        */
   10175             : /************************************************************************/
   10176             : 
   10177         632 : GDALExtendedDataType::GDALExtendedDataType(
   10178             :     const std::string &osName, size_t nTotalSize,
   10179         632 :     std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
   10180             :     : m_osName(osName), m_eClass(GEDTC_COMPOUND),
   10181         632 :       m_aoComponents(std::move(components)), m_nSize(nTotalSize)
   10182             : {
   10183         632 : }
   10184             : 
   10185             : /************************************************************************/
   10186             : /*                        GDALExtendedDataType()                        */
   10187             : /************************************************************************/
   10188             : 
   10189             : /** Copy constructor. */
   10190       15843 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
   10191       31686 :     : m_osName(other.m_osName), m_eClass(other.m_eClass),
   10192       15843 :       m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
   10193       15843 :       m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength)
   10194             : {
   10195       15843 :     if (m_eClass == GEDTC_COMPOUND)
   10196             :     {
   10197         431 :         for (const auto &elt : other.m_aoComponents)
   10198             :         {
   10199         281 :             m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
   10200             :         }
   10201             :     }
   10202       15843 : }
   10203             : 
   10204             : /************************************************************************/
   10205             : /*                            operator= ()                              */
   10206             : /************************************************************************/
   10207             : 
   10208             : /** Copy assignment. */
   10209             : GDALExtendedDataType &
   10210        1045 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
   10211             : {
   10212        1045 :     if (this != &other)
   10213             :     {
   10214        1045 :         m_osName = other.m_osName;
   10215        1045 :         m_eClass = other.m_eClass;
   10216        1045 :         m_eSubType = other.m_eSubType;
   10217        1045 :         m_eNumericDT = other.m_eNumericDT;
   10218        1045 :         m_nSize = other.m_nSize;
   10219        1045 :         m_nMaxStringLength = other.m_nMaxStringLength;
   10220        1045 :         m_aoComponents.clear();
   10221        1045 :         if (m_eClass == GEDTC_COMPOUND)
   10222             :         {
   10223           0 :             for (const auto &elt : other.m_aoComponents)
   10224             :             {
   10225           0 :                 m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
   10226             :             }
   10227             :         }
   10228             :     }
   10229        1045 :     return *this;
   10230             : }
   10231             : 
   10232             : /************************************************************************/
   10233             : /*                            operator= ()                              */
   10234             : /************************************************************************/
   10235             : 
   10236             : /** Move assignment. */
   10237             : GDALExtendedDataType &
   10238       14731 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other)
   10239             : {
   10240       14731 :     m_osName = std::move(other.m_osName);
   10241       14731 :     m_eClass = other.m_eClass;
   10242       14731 :     m_eSubType = other.m_eSubType;
   10243       14731 :     m_eNumericDT = other.m_eNumericDT;
   10244       14731 :     m_nSize = other.m_nSize;
   10245       14731 :     m_nMaxStringLength = other.m_nMaxStringLength;
   10246       14731 :     m_aoComponents = std::move(other.m_aoComponents);
   10247       14731 :     other.m_eClass = GEDTC_NUMERIC;
   10248       14731 :     other.m_eNumericDT = GDT_Unknown;
   10249       14731 :     other.m_nSize = 0;
   10250       14731 :     other.m_nMaxStringLength = 0;
   10251       14731 :     return *this;
   10252             : }
   10253             : 
   10254             : /************************************************************************/
   10255             : /*                           Create()                                   */
   10256             : /************************************************************************/
   10257             : 
   10258             : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
   10259             :  *
   10260             :  * This is the same as the C function GDALExtendedDataTypeCreate()
   10261             :  *
   10262             :  * @param eType Numeric data type. Must be different from GDT_Unknown and
   10263             :  * GDT_TypeCount
   10264             :  */
   10265       35847 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
   10266             : {
   10267       35847 :     return GDALExtendedDataType(eType);
   10268             : }
   10269             : 
   10270             : /************************************************************************/
   10271             : /*                           Create()                                   */
   10272             : /************************************************************************/
   10273             : 
   10274             : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
   10275             :  *
   10276             :  * This is the same as the C function GDALExtendedDataTypeCreateCompound()
   10277             :  *
   10278             :  * @param osName Type name.
   10279             :  * @param nTotalSize Total size of the type in bytes.
   10280             :  *                   Should be large enough to store all components.
   10281             :  * @param components Components of the compound type.
   10282             :  */
   10283         639 : GDALExtendedDataType GDALExtendedDataType::Create(
   10284             :     const std::string &osName, size_t nTotalSize,
   10285             :     std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
   10286             : {
   10287         639 :     size_t nLastOffset = 0;
   10288             :     // Some arbitrary threshold to avoid potential integer overflows
   10289         639 :     if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
   10290             :     {
   10291           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10292           2 :         return GDALExtendedDataType(GDT_Unknown);
   10293             :     }
   10294        3155 :     for (const auto &comp : components)
   10295             :     {
   10296             :         // Check alignment too ?
   10297        2519 :         if (comp->GetOffset() < nLastOffset)
   10298             :         {
   10299           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10300           1 :             return GDALExtendedDataType(GDT_Unknown);
   10301             :         }
   10302        2518 :         nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
   10303             :     }
   10304         636 :     if (nTotalSize < nLastOffset)
   10305             :     {
   10306           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10307           1 :         return GDALExtendedDataType(GDT_Unknown);
   10308             :     }
   10309         635 :     if (nTotalSize == 0 || components.empty())
   10310             :     {
   10311           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
   10312           3 :         return GDALExtendedDataType(GDT_Unknown);
   10313             :     }
   10314         632 :     return GDALExtendedDataType(osName, nTotalSize, std::move(components));
   10315             : }
   10316             : 
   10317             : /************************************************************************/
   10318             : /*                           Create()                                   */
   10319             : /************************************************************************/
   10320             : 
   10321             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   10322             :  *
   10323             :  * This is the same as the C function GDALExtendedDataTypeCreateString().
   10324             :  *
   10325             :  * @param nMaxStringLength maximum length of a string in bytes. 0 if
   10326             :  * unknown/unlimited
   10327             :  * @param eSubType Subtype.
   10328             :  */
   10329             : GDALExtendedDataType
   10330        8328 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
   10331             :                                    GDALExtendedDataTypeSubType eSubType)
   10332             : {
   10333        8328 :     return GDALExtendedDataType(nMaxStringLength, eSubType);
   10334             : }
   10335             : 
   10336             : /************************************************************************/
   10337             : /*                           operator==()                               */
   10338             : /************************************************************************/
   10339             : 
   10340             : /** Equality operator.
   10341             :  *
   10342             :  * This is the same as the C function GDALExtendedDataTypeEquals().
   10343             :  */
   10344        2242 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
   10345             : {
   10346        2215 :     if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
   10347        4457 :         m_nSize != other.m_nSize || m_osName != other.m_osName)
   10348             :     {
   10349         177 :         return false;
   10350             :     }
   10351        2065 :     if (m_eClass == GEDTC_NUMERIC)
   10352             :     {
   10353         868 :         return m_eNumericDT == other.m_eNumericDT;
   10354             :     }
   10355        1197 :     if (m_eClass == GEDTC_STRING)
   10356             :     {
   10357        1016 :         return true;
   10358             :     }
   10359         181 :     CPLAssert(m_eClass == GEDTC_COMPOUND);
   10360         181 :     if (m_aoComponents.size() != other.m_aoComponents.size())
   10361             :     {
   10362           2 :         return false;
   10363             :     }
   10364         806 :     for (size_t i = 0; i < m_aoComponents.size(); i++)
   10365             :     {
   10366         627 :         if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
   10367             :         {
   10368           0 :             return false;
   10369             :         }
   10370             :     }
   10371         179 :     return true;
   10372             : }
   10373             : 
   10374             : /************************************************************************/
   10375             : /*                        CanConvertTo()                                */
   10376             : /************************************************************************/
   10377             : 
   10378             : /** Return whether this data type can be converted to the other one.
   10379             :  *
   10380             :  * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
   10381             :  *
   10382             :  * @param other Target data type for the conversion being considered.
   10383             :  */
   10384        8161 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
   10385             : {
   10386        8161 :     if (m_eClass == GEDTC_NUMERIC)
   10387             :     {
   10388        5802 :         if (m_eNumericDT == GDT_Unknown)
   10389           0 :             return false;
   10390        5802 :         if (other.m_eClass == GEDTC_NUMERIC &&
   10391        5715 :             other.m_eNumericDT == GDT_Unknown)
   10392           0 :             return false;
   10393        5889 :         return other.m_eClass == GEDTC_NUMERIC ||
   10394        5889 :                other.m_eClass == GEDTC_STRING;
   10395             :     }
   10396        2359 :     if (m_eClass == GEDTC_STRING)
   10397             :     {
   10398        2220 :         return other.m_eClass == m_eClass;
   10399             :     }
   10400         139 :     CPLAssert(m_eClass == GEDTC_COMPOUND);
   10401         139 :     if (other.m_eClass != GEDTC_COMPOUND)
   10402           0 :         return false;
   10403             :     std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
   10404         278 :         srcComponents;
   10405         568 :     for (const auto &srcComp : m_aoComponents)
   10406             :     {
   10407         429 :         srcComponents[srcComp->GetName()] = &srcComp;
   10408             :     }
   10409         419 :     for (const auto &dstComp : other.m_aoComponents)
   10410             :     {
   10411         281 :         auto oIter = srcComponents.find(dstComp->GetName());
   10412         281 :         if (oIter == srcComponents.end())
   10413           1 :             return false;
   10414         280 :         if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
   10415           0 :             return false;
   10416             :     }
   10417         138 :     return true;
   10418             : }
   10419             : 
   10420             : /************************************************************************/
   10421             : /*                     NeedsFreeDynamicMemory()                         */
   10422             : /************************************************************************/
   10423             : 
   10424             : /** Return whether the data type holds dynamically allocated memory, that
   10425             :  * needs to be freed with FreeDynamicMemory().
   10426             :  *
   10427             :  */
   10428        3539 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
   10429             : {
   10430        3539 :     switch (m_eClass)
   10431             :     {
   10432         852 :         case GEDTC_STRING:
   10433         852 :             return true;
   10434             : 
   10435        2614 :         case GEDTC_NUMERIC:
   10436        2614 :             return false;
   10437             : 
   10438          73 :         case GEDTC_COMPOUND:
   10439             :         {
   10440         186 :             for (const auto &comp : m_aoComponents)
   10441             :             {
   10442         172 :                 if (comp->GetType().NeedsFreeDynamicMemory())
   10443          59 :                     return true;
   10444             :             }
   10445             :         }
   10446             :     }
   10447          14 :     return false;
   10448             : }
   10449             : 
   10450             : /************************************************************************/
   10451             : /*                        FreeDynamicMemory()                           */
   10452             : /************************************************************************/
   10453             : 
   10454             : /** Release the dynamic memory (strings typically) from a raw value.
   10455             :  *
   10456             :  * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
   10457             :  *
   10458             :  * @param pBuffer Raw buffer of a single element of an attribute or array value.
   10459             :  */
   10460        3298 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
   10461             : {
   10462        3298 :     switch (m_eClass)
   10463             :     {
   10464        2306 :         case GEDTC_STRING:
   10465             :         {
   10466             :             char *pszStr;
   10467        2306 :             memcpy(&pszStr, pBuffer, sizeof(char *));
   10468        2306 :             if (pszStr)
   10469             :             {
   10470        1795 :                 VSIFree(pszStr);
   10471             :             }
   10472        2306 :             break;
   10473             :         }
   10474             : 
   10475         853 :         case GEDTC_NUMERIC:
   10476             :         {
   10477         853 :             break;
   10478             :         }
   10479             : 
   10480         139 :         case GEDTC_COMPOUND:
   10481             :         {
   10482         139 :             GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
   10483         605 :             for (const auto &comp : m_aoComponents)
   10484             :             {
   10485         932 :                 comp->GetType().FreeDynamicMemory(pabyBuffer +
   10486         466 :                                                   comp->GetOffset());
   10487             :             }
   10488         139 :             break;
   10489             :         }
   10490             :     }
   10491        3298 : }
   10492             : 
   10493             : /************************************************************************/
   10494             : /*                      ~GDALEDTComponent()                             */
   10495             : /************************************************************************/
   10496             : 
   10497             : GDALEDTComponent::~GDALEDTComponent() = default;
   10498             : 
   10499             : /************************************************************************/
   10500             : /*                      GDALEDTComponent()                              */
   10501             : /************************************************************************/
   10502             : 
   10503             : /** constructor of a GDALEDTComponent
   10504             :  *
   10505             :  * This is the same as the C function GDALEDTComponendCreate()
   10506             :  *
   10507             :  * @param name Component name
   10508             :  * @param offset Offset in byte of the component in the compound data type.
   10509             :  *               In case of nesting of compound data type, this should be
   10510             :  *               the offset to the immediate belonging data type, not to the
   10511             :  *               higher level one.
   10512             :  * @param type   Component data type.
   10513             :  */
   10514        2510 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
   10515        2510 :                                    const GDALExtendedDataType &type)
   10516        2510 :     : m_osName(name), m_nOffset(offset), m_oType(type)
   10517             : {
   10518        2510 : }
   10519             : 
   10520             : /************************************************************************/
   10521             : /*                      GDALEDTComponent()                              */
   10522             : /************************************************************************/
   10523             : 
   10524             : /** Copy constructor. */
   10525             : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
   10526             : 
   10527             : /************************************************************************/
   10528             : /*                           operator==()                               */
   10529             : /************************************************************************/
   10530             : 
   10531             : /** Equality operator.
   10532             :  */
   10533         627 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
   10534             : {
   10535        1254 :     return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
   10536        1254 :            m_oType == other.m_oType;
   10537             : }
   10538             : 
   10539             : /************************************************************************/
   10540             : /*                        ~GDALDimension()                              */
   10541             : /************************************************************************/
   10542             : 
   10543             : GDALDimension::~GDALDimension() = default;
   10544             : 
   10545             : /************************************************************************/
   10546             : /*                         GDALDimension()                              */
   10547             : /************************************************************************/
   10548             : 
   10549             : //! @cond Doxygen_Suppress
   10550             : /** Constructor.
   10551             :  *
   10552             :  * @param osParentName Parent name
   10553             :  * @param osName name
   10554             :  * @param osType type. See GetType().
   10555             :  * @param osDirection direction. See GetDirection().
   10556             :  * @param nSize size.
   10557             :  */
   10558        8175 : GDALDimension::GDALDimension(const std::string &osParentName,
   10559             :                              const std::string &osName,
   10560             :                              const std::string &osType,
   10561        8175 :                              const std::string &osDirection, GUInt64 nSize)
   10562             :     : m_osName(osName),
   10563             :       m_osFullName(
   10564        8175 :           !osParentName.empty()
   10565       12084 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
   10566             :               : osName),
   10567       28434 :       m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
   10568             : {
   10569        8175 : }
   10570             : 
   10571             : //! @endcond
   10572             : 
   10573             : /************************************************************************/
   10574             : /*                         GetIndexingVariable()                        */
   10575             : /************************************************************************/
   10576             : 
   10577             : /** Return the variable that is used to index the dimension (if there is one).
   10578             :  *
   10579             :  * This is the array, typically one-dimensional, describing the values taken
   10580             :  * by the dimension.
   10581             :  */
   10582          49 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
   10583             : {
   10584          49 :     return nullptr;
   10585             : }
   10586             : 
   10587             : /************************************************************************/
   10588             : /*                         SetIndexingVariable()                        */
   10589             : /************************************************************************/
   10590             : 
   10591             : /** Set the variable that is used to index the dimension.
   10592             :  *
   10593             :  * This is the array, typically one-dimensional, describing the values taken
   10594             :  * by the dimension.
   10595             :  *
   10596             :  * Optionally implemented by drivers.
   10597             :  *
   10598             :  * Drivers known to implement it: MEM.
   10599             :  *
   10600             :  * @param poArray Variable to use to index the dimension.
   10601             :  * @return true in case of success.
   10602             :  */
   10603           3 : bool GDALDimension::SetIndexingVariable(
   10604             :     CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
   10605             : {
   10606           3 :     CPLError(CE_Failure, CPLE_NotSupported,
   10607             :              "SetIndexingVariable() not implemented");
   10608           3 :     return false;
   10609             : }
   10610             : 
   10611             : /************************************************************************/
   10612             : /*                            Rename()                                  */
   10613             : /************************************************************************/
   10614             : 
   10615             : /** Rename the dimension.
   10616             :  *
   10617             :  * This is not implemented by all drivers.
   10618             :  *
   10619             :  * Drivers known to implement it: MEM, netCDF, ZARR.
   10620             :  *
   10621             :  * This is the same as the C function GDALDimensionRename().
   10622             :  *
   10623             :  * @param osNewName New name.
   10624             :  *
   10625             :  * @return true in case of success
   10626             :  * @since GDAL 3.8
   10627             :  */
   10628           0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
   10629             : {
   10630           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
   10631           0 :     return false;
   10632             : }
   10633             : 
   10634             : /************************************************************************/
   10635             : /*                         BaseRename()                                 */
   10636             : /************************************************************************/
   10637             : 
   10638             : //! @cond Doxygen_Suppress
   10639           8 : void GDALDimension::BaseRename(const std::string &osNewName)
   10640             : {
   10641           8 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
   10642           8 :     m_osFullName += osNewName;
   10643           8 :     m_osName = osNewName;
   10644           8 : }
   10645             : 
   10646             : //! @endcond
   10647             : 
   10648             : //! @cond Doxygen_Suppress
   10649             : /************************************************************************/
   10650             : /*                          ParentRenamed()                             */
   10651             : /************************************************************************/
   10652             : 
   10653           8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
   10654             : {
   10655           8 :     m_osFullName = osNewParentFullName;
   10656           8 :     m_osFullName += "/";
   10657           8 :     m_osFullName += m_osName;
   10658           8 : }
   10659             : 
   10660             : //! @endcond
   10661             : 
   10662             : //! @cond Doxygen_Suppress
   10663             : /************************************************************************/
   10664             : /*                          ParentDeleted()                             */
   10665             : /************************************************************************/
   10666             : 
   10667           4 : void GDALDimension::ParentDeleted()
   10668             : {
   10669           4 : }
   10670             : 
   10671             : //! @endcond
   10672             : 
   10673             : /************************************************************************/
   10674             : /************************************************************************/
   10675             : /************************************************************************/
   10676             : /*                              C API                                   */
   10677             : /************************************************************************/
   10678             : /************************************************************************/
   10679             : /************************************************************************/
   10680             : 
   10681             : /************************************************************************/
   10682             : /*                      GDALExtendedDataTypeCreate()                    */
   10683             : /************************************************************************/
   10684             : 
   10685             : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
   10686             :  *
   10687             :  * This is the same as the C++ method GDALExtendedDataType::Create()
   10688             :  *
   10689             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10690             :  *
   10691             :  * @param eType Numeric data type. Must be different from GDT_Unknown and
   10692             :  * GDT_TypeCount
   10693             :  *
   10694             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10695             :  */
   10696        1992 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
   10697             : {
   10698        1992 :     if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
   10699             :     {
   10700           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
   10701             :                  "Illegal GDT_Unknown/GDT_TypeCount argument");
   10702           0 :         return nullptr;
   10703             :     }
   10704             :     return new GDALExtendedDataTypeHS(
   10705        1992 :         new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
   10706             : }
   10707             : 
   10708             : /************************************************************************/
   10709             : /*                    GDALExtendedDataTypeCreateString()                */
   10710             : /************************************************************************/
   10711             : 
   10712             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   10713             :  *
   10714             :  * This is the same as the C++ method GDALExtendedDataType::CreateString()
   10715             :  *
   10716             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10717             :  *
   10718             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10719             :  */
   10720           0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
   10721             : {
   10722           0 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   10723           0 :         GDALExtendedDataType::CreateString(nMaxStringLength)));
   10724             : }
   10725             : 
   10726             : /************************************************************************/
   10727             : /*                   GDALExtendedDataTypeCreateStringEx()               */
   10728             : /************************************************************************/
   10729             : 
   10730             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   10731             :  *
   10732             :  * This is the same as the C++ method GDALExtendedDataType::CreateString()
   10733             :  *
   10734             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10735             :  *
   10736             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10737             :  * @since GDAL 3.4
   10738             :  */
   10739             : GDALExtendedDataTypeH
   10740         188 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
   10741             :                                    GDALExtendedDataTypeSubType eSubType)
   10742             : {
   10743         188 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   10744         188 :         GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
   10745             : }
   10746             : 
   10747             : /************************************************************************/
   10748             : /*                   GDALExtendedDataTypeCreateCompound()               */
   10749             : /************************************************************************/
   10750             : 
   10751             : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
   10752             :  *
   10753             :  * This is the same as the C++ method GDALExtendedDataType::Create(const
   10754             :  * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
   10755             :  *
   10756             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10757             :  *
   10758             :  * @param pszName Type name.
   10759             :  * @param nTotalSize Total size of the type in bytes.
   10760             :  *                   Should be large enough to store all components.
   10761             :  * @param nComponents Number of components in comps array.
   10762             :  * @param comps Components.
   10763             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10764             :  */
   10765             : GDALExtendedDataTypeH
   10766          22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
   10767             :                                    size_t nComponents,
   10768             :                                    const GDALEDTComponentH *comps)
   10769             : {
   10770          44 :     std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
   10771          54 :     for (size_t i = 0; i < nComponents; i++)
   10772             :     {
   10773          64 :         compsCpp.emplace_back(std::unique_ptr<GDALEDTComponent>(
   10774          64 :             new GDALEDTComponent(*(comps[i]->m_poImpl.get()))));
   10775             :     }
   10776             :     auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
   10777          66 :                                            std::move(compsCpp));
   10778          22 :     if (dt.GetClass() != GEDTC_COMPOUND)
   10779           6 :         return nullptr;
   10780          16 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(dt));
   10781             : }
   10782             : 
   10783             : /************************************************************************/
   10784             : /*                     GDALExtendedDataTypeRelease()                    */
   10785             : /************************************************************************/
   10786             : 
   10787             : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
   10788             :  *
   10789             :  * Note: when applied on a object coming from a driver, this does not
   10790             :  * destroy the object in the file, database, etc...
   10791             :  */
   10792        6541 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
   10793             : {
   10794        6541 :     delete hEDT;
   10795        6541 : }
   10796             : 
   10797             : /************************************************************************/
   10798             : /*                     GDALExtendedDataTypeGetName()                    */
   10799             : /************************************************************************/
   10800             : 
   10801             : /** Return type name.
   10802             :  *
   10803             :  * This is the same as the C++ method GDALExtendedDataType::GetName()
   10804             :  */
   10805           7 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
   10806             : {
   10807           7 :     VALIDATE_POINTER1(hEDT, __func__, "");
   10808           7 :     return hEDT->m_poImpl->GetName().c_str();
   10809             : }
   10810             : 
   10811             : /************************************************************************/
   10812             : /*                     GDALExtendedDataTypeGetClass()                    */
   10813             : /************************************************************************/
   10814             : 
   10815             : /** Return type class.
   10816             :  *
   10817             :  * This is the same as the C++ method GDALExtendedDataType::GetClass()
   10818             :  */
   10819             : GDALExtendedDataTypeClass
   10820        9029 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
   10821             : {
   10822        9029 :     VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
   10823        9029 :     return hEDT->m_poImpl->GetClass();
   10824             : }
   10825             : 
   10826             : /************************************************************************/
   10827             : /*               GDALExtendedDataTypeGetNumericDataType()               */
   10828             : /************************************************************************/
   10829             : 
   10830             : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
   10831             :  *
   10832             :  * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
   10833             :  */
   10834        2010 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
   10835             : {
   10836        2010 :     VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
   10837        2010 :     return hEDT->m_poImpl->GetNumericDataType();
   10838             : }
   10839             : 
   10840             : /************************************************************************/
   10841             : /*                   GDALExtendedDataTypeGetSize()                      */
   10842             : /************************************************************************/
   10843             : 
   10844             : /** Return data type size in bytes.
   10845             :  *
   10846             :  * This is the same as the C++ method GDALExtendedDataType::GetSize()
   10847             :  */
   10848        2510 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
   10849             : {
   10850        2510 :     VALIDATE_POINTER1(hEDT, __func__, 0);
   10851        2510 :     return hEDT->m_poImpl->GetSize();
   10852             : }
   10853             : 
   10854             : /************************************************************************/
   10855             : /*              GDALExtendedDataTypeGetMaxStringLength()                */
   10856             : /************************************************************************/
   10857             : 
   10858             : /** Return the maximum length of a string in bytes.
   10859             :  *
   10860             :  * 0 indicates unknown/unlimited string.
   10861             :  *
   10862             :  * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
   10863             :  */
   10864           3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
   10865             : {
   10866           3 :     VALIDATE_POINTER1(hEDT, __func__, 0);
   10867           3 :     return hEDT->m_poImpl->GetMaxStringLength();
   10868             : }
   10869             : 
   10870             : /************************************************************************/
   10871             : /*                    GDALExtendedDataTypeCanConvertTo()                */
   10872             : /************************************************************************/
   10873             : 
   10874             : /** Return whether this data type can be converted to the other one.
   10875             :  *
   10876             :  * This is the same as the C function GDALExtendedDataType::CanConvertTo()
   10877             :  *
   10878             :  * @param hSourceEDT Source data type for the conversion being considered.
   10879             :  * @param hTargetEDT Target data type for the conversion being considered.
   10880             :  * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
   10881             :  */
   10882           7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
   10883             :                                      GDALExtendedDataTypeH hTargetEDT)
   10884             : {
   10885           7 :     VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
   10886           7 :     VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
   10887           7 :     return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
   10888             : }
   10889             : 
   10890             : /************************************************************************/
   10891             : /*                        GDALExtendedDataTypeEquals()                  */
   10892             : /************************************************************************/
   10893             : 
   10894             : /** Return whether this data type is equal to another one.
   10895             :  *
   10896             :  * This is the same as the C++ method GDALExtendedDataType::operator==()
   10897             :  *
   10898             :  * @param hFirstEDT First data type.
   10899             :  * @param hSecondEDT Second data type.
   10900             :  * @return TRUE if they are equal. FALSE otherwise.
   10901             :  */
   10902         100 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
   10903             :                                GDALExtendedDataTypeH hSecondEDT)
   10904             : {
   10905         100 :     VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
   10906         100 :     VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
   10907         100 :     return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
   10908             : }
   10909             : 
   10910             : /************************************************************************/
   10911             : /*                    GDALExtendedDataTypeGetSubType()                  */
   10912             : /************************************************************************/
   10913             : 
   10914             : /** Return the subtype of a type.
   10915             :  *
   10916             :  * This is the same as the C++ method GDALExtendedDataType::GetSubType()
   10917             :  *
   10918             :  * @param hEDT Data type.
   10919             :  * @return subtype.
   10920             :  * @since 3.4
   10921             :  */
   10922             : GDALExtendedDataTypeSubType
   10923         104 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
   10924             : {
   10925         104 :     VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
   10926         104 :     return hEDT->m_poImpl->GetSubType();
   10927             : }
   10928             : 
   10929             : /************************************************************************/
   10930             : /*                     GDALExtendedDataTypeGetComponents()              */
   10931             : /************************************************************************/
   10932             : 
   10933             : /** Return the components of the data type (only valid when GetClass() ==
   10934             :  * GEDTC_COMPOUND)
   10935             :  *
   10936             :  * The returned array and its content must be freed with
   10937             :  * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
   10938             :  * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
   10939             :  * individual array members).
   10940             :  *
   10941             :  * This is the same as the C++ method GDALExtendedDataType::GetComponents()
   10942             :  *
   10943             :  * @param hEDT Data type
   10944             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   10945             :  * @return an array of *pnCount components.
   10946             :  */
   10947          44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
   10948             :                                                      size_t *pnCount)
   10949             : {
   10950          44 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   10951          44 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   10952          44 :     const auto &components = hEDT->m_poImpl->GetComponents();
   10953             :     auto ret = static_cast<GDALEDTComponentH *>(
   10954          44 :         CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
   10955         131 :     for (size_t i = 0; i < components.size(); i++)
   10956             :     {
   10957          87 :         ret[i] = new GDALEDTComponentHS(*components[i].get());
   10958             :     }
   10959          44 :     *pnCount = components.size();
   10960          44 :     return ret;
   10961             : }
   10962             : 
   10963             : /************************************************************************/
   10964             : /*                     GDALExtendedDataTypeFreeComponents()             */
   10965             : /************************************************************************/
   10966             : 
   10967             : /** Free the return of GDALExtendedDataTypeGetComponents().
   10968             :  *
   10969             :  * @param components return value of GDALExtendedDataTypeGetComponents()
   10970             :  * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
   10971             :  */
   10972          44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
   10973             :                                         size_t nCount)
   10974             : {
   10975         131 :     for (size_t i = 0; i < nCount; i++)
   10976             :     {
   10977          87 :         delete components[i];
   10978             :     }
   10979          44 :     CPLFree(components);
   10980          44 : }
   10981             : 
   10982             : /************************************************************************/
   10983             : /*                         GDALEDTComponentCreate()                     */
   10984             : /************************************************************************/
   10985             : 
   10986             : /** Create a new GDALEDTComponent.
   10987             :  *
   10988             :  * The returned value must be freed with GDALEDTComponentRelease().
   10989             :  *
   10990             :  * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
   10991             :  */
   10992          20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
   10993             :                                          GDALExtendedDataTypeH hType)
   10994             : {
   10995          20 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   10996          20 :     VALIDATE_POINTER1(hType, __func__, nullptr);
   10997             :     return new GDALEDTComponentHS(
   10998          20 :         GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
   10999             : }
   11000             : 
   11001             : /************************************************************************/
   11002             : /*                         GDALEDTComponentRelease()                    */
   11003             : /************************************************************************/
   11004             : 
   11005             : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
   11006             :  *
   11007             :  * Note: when applied on a object coming from a driver, this does not
   11008             :  * destroy the object in the file, database, etc...
   11009             :  */
   11010          61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
   11011             : {
   11012          61 :     delete hComp;
   11013          61 : }
   11014             : 
   11015             : /************************************************************************/
   11016             : /*                         GDALEDTComponentGetName()                    */
   11017             : /************************************************************************/
   11018             : 
   11019             : /** Return the name.
   11020             :  *
   11021             :  * The returned pointer is valid until hComp is released.
   11022             :  *
   11023             :  * This is the same as the C++ method GDALEDTComponent::GetName().
   11024             :  */
   11025          33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
   11026             : {
   11027          33 :     VALIDATE_POINTER1(hComp, __func__, nullptr);
   11028          33 :     return hComp->m_poImpl->GetName().c_str();
   11029             : }
   11030             : 
   11031             : /************************************************************************/
   11032             : /*                       GDALEDTComponentGetOffset()                    */
   11033             : /************************************************************************/
   11034             : 
   11035             : /** Return the offset (in bytes) of the component in the compound data type.
   11036             :  *
   11037             :  * This is the same as the C++ method GDALEDTComponent::GetOffset().
   11038             :  */
   11039          31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
   11040             : {
   11041          31 :     VALIDATE_POINTER1(hComp, __func__, 0);
   11042          31 :     return hComp->m_poImpl->GetOffset();
   11043             : }
   11044             : 
   11045             : /************************************************************************/
   11046             : /*                       GDALEDTComponentGetType()                      */
   11047             : /************************************************************************/
   11048             : 
   11049             : /** Return the data type of the component.
   11050             :  *
   11051             :  * This is the same as the C++ method GDALEDTComponent::GetType().
   11052             :  */
   11053          93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
   11054             : {
   11055          93 :     VALIDATE_POINTER1(hComp, __func__, nullptr);
   11056             :     return new GDALExtendedDataTypeHS(
   11057          93 :         new GDALExtendedDataType(hComp->m_poImpl->GetType()));
   11058             : }
   11059             : 
   11060             : /************************************************************************/
   11061             : /*                           GDALGroupRelease()                         */
   11062             : /************************************************************************/
   11063             : 
   11064             : /** Release the GDAL in-memory object associated with a GDALGroupH.
   11065             :  *
   11066             :  * Note: when applied on a object coming from a driver, this does not
   11067             :  * destroy the object in the file, database, etc...
   11068             :  */
   11069        1405 : void GDALGroupRelease(GDALGroupH hGroup)
   11070             : {
   11071        1405 :     delete hGroup;
   11072        1405 : }
   11073             : 
   11074             : /************************************************************************/
   11075             : /*                           GDALGroupGetName()                         */
   11076             : /************************************************************************/
   11077             : 
   11078             : /** Return the name of the group.
   11079             :  *
   11080             :  * The returned pointer is valid until hGroup is released.
   11081             :  *
   11082             :  * This is the same as the C++ method GDALGroup::GetName().
   11083             :  */
   11084          95 : const char *GDALGroupGetName(GDALGroupH hGroup)
   11085             : {
   11086          95 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11087          95 :     return hGroup->m_poImpl->GetName().c_str();
   11088             : }
   11089             : 
   11090             : /************************************************************************/
   11091             : /*                         GDALGroupGetFullName()                       */
   11092             : /************************************************************************/
   11093             : 
   11094             : /** Return the full name of the group.
   11095             :  *
   11096             :  * The returned pointer is valid until hGroup is released.
   11097             :  *
   11098             :  * This is the same as the C++ method GDALGroup::GetFullName().
   11099             :  */
   11100          47 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
   11101             : {
   11102          47 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11103          47 :     return hGroup->m_poImpl->GetFullName().c_str();
   11104             : }
   11105             : 
   11106             : /************************************************************************/
   11107             : /*                          GDALGroupGetMDArrayNames()                  */
   11108             : /************************************************************************/
   11109             : 
   11110             : /** Return the list of multidimensional array names contained in this group.
   11111             :  *
   11112             :  * This is the same as the C++ method GDALGroup::GetGroupNames().
   11113             :  *
   11114             :  * @return the array names, to be freed with CSLDestroy()
   11115             :  */
   11116         325 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
   11117             : {
   11118         325 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11119         650 :     auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
   11120         650 :     CPLStringList res;
   11121         821 :     for (const auto &name : names)
   11122             :     {
   11123         496 :         res.AddString(name.c_str());
   11124             :     }
   11125         325 :     return res.StealList();
   11126             : }
   11127             : 
   11128             : /************************************************************************/
   11129             : /*                          GDALGroupOpenMDArray()                      */
   11130             : /************************************************************************/
   11131             : 
   11132             : /** Open and return a multidimensional array.
   11133             :  *
   11134             :  * This is the same as the C++ method GDALGroup::OpenMDArray().
   11135             :  *
   11136             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11137             :  */
   11138         777 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
   11139             :                                   CSLConstList papszOptions)
   11140             : {
   11141         777 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11142         777 :     VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
   11143        2331 :     auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
   11144        2331 :                                                papszOptions);
   11145         777 :     if (!array)
   11146          30 :         return nullptr;
   11147         747 :     return new GDALMDArrayHS(array);
   11148             : }
   11149             : 
   11150             : /************************************************************************/
   11151             : /*                  GDALGroupOpenMDArrayFromFullname()                  */
   11152             : /************************************************************************/
   11153             : 
   11154             : /** Open and return a multidimensional array from its fully qualified name.
   11155             :  *
   11156             :  * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
   11157             :  *
   11158             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11159             :  *
   11160             :  * @since GDAL 3.2
   11161             :  */
   11162          16 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
   11163             :                                               const char *pszFullname,
   11164             :                                               CSLConstList papszOptions)
   11165             : {
   11166          16 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11167          16 :     VALIDATE_POINTER1(pszFullname, __func__, nullptr);
   11168          16 :     auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
   11169          48 :         std::string(pszFullname), papszOptions);
   11170          16 :     if (!array)
   11171           2 :         return nullptr;
   11172          14 :     return new GDALMDArrayHS(array);
   11173             : }
   11174             : 
   11175             : /************************************************************************/
   11176             : /*                      GDALGroupResolveMDArray()                       */
   11177             : /************************************************************************/
   11178             : 
   11179             : /** Locate an array in a group and its subgroups by name.
   11180             :  *
   11181             :  * See GDALGroup::ResolveMDArray() for description of the behavior.
   11182             :  * @since GDAL 3.2
   11183             :  */
   11184          19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
   11185             :                                      const char *pszStartingPoint,
   11186             :                                      CSLConstList papszOptions)
   11187             : {
   11188          19 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11189          19 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11190          19 :     VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
   11191          19 :     auto array = hGroup->m_poImpl->ResolveMDArray(
   11192          57 :         std::string(pszName), std::string(pszStartingPoint), papszOptions);
   11193          19 :     if (!array)
   11194           2 :         return nullptr;
   11195          17 :     return new GDALMDArrayHS(array);
   11196             : }
   11197             : 
   11198             : /************************************************************************/
   11199             : /*                        GDALGroupGetGroupNames()                      */
   11200             : /************************************************************************/
   11201             : 
   11202             : /** Return the list of sub-groups contained in this group.
   11203             :  *
   11204             :  * This is the same as the C++ method GDALGroup::GetGroupNames().
   11205             :  *
   11206             :  * @return the group names, to be freed with CSLDestroy()
   11207             :  */
   11208          97 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
   11209             : {
   11210          97 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11211         194 :     auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
   11212         194 :     CPLStringList res;
   11213         219 :     for (const auto &name : names)
   11214             :     {
   11215         122 :         res.AddString(name.c_str());
   11216             :     }
   11217          97 :     return res.StealList();
   11218             : }
   11219             : 
   11220             : /************************************************************************/
   11221             : /*                           GDALGroupOpenGroup()                       */
   11222             : /************************************************************************/
   11223             : 
   11224             : /** Open and return a sub-group.
   11225             :  *
   11226             :  * This is the same as the C++ method GDALGroup::OpenGroup().
   11227             :  *
   11228             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11229             :  */
   11230         163 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11231             :                               CSLConstList papszOptions)
   11232             : {
   11233         163 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11234         163 :     VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
   11235             :     auto subGroup =
   11236         489 :         hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
   11237         163 :     if (!subGroup)
   11238          30 :         return nullptr;
   11239         133 :     return new GDALGroupHS(subGroup);
   11240             : }
   11241             : 
   11242             : /************************************************************************/
   11243             : /*                   GDALGroupGetVectorLayerNames()                     */
   11244             : /************************************************************************/
   11245             : 
   11246             : /** Return the list of layer names contained in this group.
   11247             :  *
   11248             :  * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
   11249             :  *
   11250             :  * @return the group names, to be freed with CSLDestroy()
   11251             :  * @since 3.4
   11252             :  */
   11253           8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
   11254             :                                     CSLConstList papszOptions)
   11255             : {
   11256           8 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11257          16 :     auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
   11258          16 :     CPLStringList res;
   11259          18 :     for (const auto &name : names)
   11260             :     {
   11261          10 :         res.AddString(name.c_str());
   11262             :     }
   11263           8 :     return res.StealList();
   11264             : }
   11265             : 
   11266             : /************************************************************************/
   11267             : /*                      GDALGroupOpenVectorLayer()                      */
   11268             : /************************************************************************/
   11269             : 
   11270             : /** Open and return a vector layer.
   11271             :  *
   11272             :  * This is the same as the C++ method GDALGroup::OpenVectorLayer().
   11273             :  *
   11274             :  * Note that the vector layer is owned by its parent GDALDatasetH, and thus
   11275             :  * the returned handled if only valid while the parent GDALDatasetH is kept
   11276             :  * opened.
   11277             :  *
   11278             :  * @return the vector layer, or nullptr.
   11279             :  * @since 3.4
   11280             :  */
   11281          12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
   11282             :                                    const char *pszVectorLayerName,
   11283             :                                    CSLConstList papszOptions)
   11284             : {
   11285          12 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11286          12 :     VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
   11287          24 :     return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
   11288          24 :         std::string(pszVectorLayerName), papszOptions));
   11289             : }
   11290             : 
   11291             : /************************************************************************/
   11292             : /*                       GDALGroupOpenMDArrayFromFullname()             */
   11293             : /************************************************************************/
   11294             : 
   11295             : /** Open and return a sub-group from its fully qualified name.
   11296             :  *
   11297             :  * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
   11298             :  *
   11299             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11300             :  *
   11301             :  * @since GDAL 3.2
   11302             :  */
   11303           3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
   11304             :                                           const char *pszFullname,
   11305             :                                           CSLConstList papszOptions)
   11306             : {
   11307           3 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11308           3 :     VALIDATE_POINTER1(pszFullname, __func__, nullptr);
   11309           3 :     auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
   11310           9 :         std::string(pszFullname), papszOptions);
   11311           3 :     if (!subGroup)
   11312           2 :         return nullptr;
   11313           1 :     return new GDALGroupHS(subGroup);
   11314             : }
   11315             : 
   11316             : /************************************************************************/
   11317             : /*                         GDALGroupGetDimensions()                     */
   11318             : /************************************************************************/
   11319             : 
   11320             : /** Return the list of dimensions contained in this group and used by its
   11321             :  * arrays.
   11322             :  *
   11323             :  * The returned array must be freed with GDALReleaseDimensions().  If only the
   11324             :  * array itself needs to be freed, CPLFree() should be called (and
   11325             :  * GDALDimensionRelease() on individual array members).
   11326             :  *
   11327             :  * This is the same as the C++ method GDALGroup::GetDimensions().
   11328             :  *
   11329             :  * @param hGroup Group.
   11330             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11331             :  * @param papszOptions Driver specific options determining how dimensions
   11332             :  * should be retrieved. Pass nullptr for default behavior.
   11333             :  *
   11334             :  * @return an array of *pnCount dimensions.
   11335             :  */
   11336          73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
   11337             :                                        CSLConstList papszOptions)
   11338             : {
   11339          73 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11340          73 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11341          73 :     auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
   11342             :     auto ret = static_cast<GDALDimensionH *>(
   11343          73 :         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
   11344         230 :     for (size_t i = 0; i < dims.size(); i++)
   11345             :     {
   11346         157 :         ret[i] = new GDALDimensionHS(dims[i]);
   11347             :     }
   11348          73 :     *pnCount = dims.size();
   11349          73 :     return ret;
   11350             : }
   11351             : 
   11352             : /************************************************************************/
   11353             : /*                          GDALGroupGetAttribute()                     */
   11354             : /************************************************************************/
   11355             : 
   11356             : /** Return an attribute by its name.
   11357             :  *
   11358             :  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
   11359             :  *
   11360             :  * The returned attribute must be freed with GDALAttributeRelease().
   11361             :  */
   11362          80 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
   11363             : {
   11364          80 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11365          80 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11366         240 :     auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
   11367          80 :     if (attr)
   11368          76 :         return new GDALAttributeHS(attr);
   11369           4 :     return nullptr;
   11370             : }
   11371             : 
   11372             : /************************************************************************/
   11373             : /*                         GDALGroupGetAttributes()                     */
   11374             : /************************************************************************/
   11375             : 
   11376             : /** Return the list of attributes contained in this group.
   11377             :  *
   11378             :  * The returned array must be freed with GDALReleaseAttributes(). If only the
   11379             :  * array itself needs to be freed, CPLFree() should be called (and
   11380             :  * GDALAttributeRelease() on individual array members).
   11381             :  *
   11382             :  * This is the same as the C++ method GDALGroup::GetAttributes().
   11383             :  *
   11384             :  * @param hGroup Group.
   11385             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11386             :  * @param papszOptions Driver specific options determining how attributes
   11387             :  * should be retrieved. Pass nullptr for default behavior.
   11388             :  *
   11389             :  * @return an array of *pnCount attributes.
   11390             :  */
   11391          71 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
   11392             :                                        CSLConstList papszOptions)
   11393             : {
   11394          71 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11395          71 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11396          71 :     auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
   11397             :     auto ret = static_cast<GDALAttributeH *>(
   11398          71 :         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
   11399         229 :     for (size_t i = 0; i < attrs.size(); i++)
   11400             :     {
   11401         158 :         ret[i] = new GDALAttributeHS(attrs[i]);
   11402             :     }
   11403          71 :     *pnCount = attrs.size();
   11404          71 :     return ret;
   11405             : }
   11406             : 
   11407             : /************************************************************************/
   11408             : /*                     GDALGroupGetStructuralInfo()                     */
   11409             : /************************************************************************/
   11410             : 
   11411             : /** Return structural information on the group.
   11412             :  *
   11413             :  * This may be the compression, etc..
   11414             :  *
   11415             :  * The return value should not be freed and is valid until GDALGroup is
   11416             :  * released or this function called again.
   11417             :  *
   11418             :  * This is the same as the C++ method GDALGroup::GetStructuralInfo().
   11419             :  */
   11420           4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
   11421             : {
   11422           4 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11423           4 :     return hGroup->m_poImpl->GetStructuralInfo();
   11424             : }
   11425             : 
   11426             : /************************************************************************/
   11427             : /*                         GDALReleaseAttributes()                      */
   11428             : /************************************************************************/
   11429             : 
   11430             : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
   11431             :  *
   11432             :  * @param attributes return pointer of above methods
   11433             :  * @param nCount *pnCount value returned by above methods
   11434             :  */
   11435         129 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
   11436             : {
   11437         416 :     for (size_t i = 0; i < nCount; i++)
   11438             :     {
   11439         287 :         delete attributes[i];
   11440             :     }
   11441         129 :     CPLFree(attributes);
   11442         129 : }
   11443             : 
   11444             : /************************************************************************/
   11445             : /*                         GDALGroupCreateGroup()                       */
   11446             : /************************************************************************/
   11447             : 
   11448             : /** Create a sub-group within a group.
   11449             :  *
   11450             :  * This is the same as the C++ method GDALGroup::CreateGroup().
   11451             :  *
   11452             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11453             :  */
   11454         173 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11455             :                                 CSLConstList papszOptions)
   11456             : {
   11457         173 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11458         173 :     VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
   11459         519 :     auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
   11460         519 :                                              papszOptions);
   11461         173 :     if (!ret)
   11462          49 :         return nullptr;
   11463         124 :     return new GDALGroupHS(ret);
   11464             : }
   11465             : 
   11466             : /************************************************************************/
   11467             : /*                         GDALGroupDeleteGroup()                       */
   11468             : /************************************************************************/
   11469             : 
   11470             : /** Delete a sub-group from a group.
   11471             :  *
   11472             :  * After this call, if a previously obtained instance of the deleted object
   11473             :  * is still alive, no method other than for freeing it should be invoked.
   11474             :  *
   11475             :  * This is the same as the C++ method GDALGroup::DeleteGroup().
   11476             :  *
   11477             :  * @return true in case of success.
   11478             :  * @since GDAL 3.8
   11479             :  */
   11480          20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11481             :                           CSLConstList papszOptions)
   11482             : {
   11483          20 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11484          20 :     VALIDATE_POINTER1(pszSubGroupName, __func__, false);
   11485          40 :     return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
   11486          20 :                                          papszOptions);
   11487             : }
   11488             : 
   11489             : /************************************************************************/
   11490             : /*                      GDALGroupCreateDimension()                      */
   11491             : /************************************************************************/
   11492             : 
   11493             : /** Create a dimension within a group.
   11494             :  *
   11495             :  * This is the same as the C++ method GDALGroup::CreateDimension().
   11496             :  *
   11497             :  * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
   11498             :  */
   11499         664 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
   11500             :                                         const char *pszType,
   11501             :                                         const char *pszDirection, GUInt64 nSize,
   11502             :                                         CSLConstList papszOptions)
   11503             : {
   11504         664 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11505         664 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11506         664 :     auto ret = hGroup->m_poImpl->CreateDimension(
   11507        1328 :         std::string(pszName), std::string(pszType ? pszType : ""),
   11508        2656 :         std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
   11509         664 :     if (!ret)
   11510           9 :         return nullptr;
   11511         655 :     return new GDALDimensionHS(ret);
   11512             : }
   11513             : 
   11514             : /************************************************************************/
   11515             : /*                      GDALGroupCreateMDArray()                        */
   11516             : /************************************************************************/
   11517             : 
   11518             : /** Create a multidimensional array within a group.
   11519             :  *
   11520             :  * This is the same as the C++ method GDALGroup::CreateMDArray().
   11521             :  *
   11522             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11523             :  */
   11524         603 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
   11525             :                                     size_t nDimensions,
   11526             :                                     GDALDimensionH *pahDimensions,
   11527             :                                     GDALExtendedDataTypeH hEDT,
   11528             :                                     CSLConstList papszOptions)
   11529             : {
   11530         603 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11531         603 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11532         603 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11533        1206 :     std::vector<std::shared_ptr<GDALDimension>> dims;
   11534         603 :     dims.reserve(nDimensions);
   11535        1423 :     for (size_t i = 0; i < nDimensions; i++)
   11536         820 :         dims.push_back(pahDimensions[i]->m_poImpl);
   11537        1809 :     auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
   11538        1809 :                                                *(hEDT->m_poImpl), papszOptions);
   11539         603 :     if (!ret)
   11540          65 :         return nullptr;
   11541         538 :     return new GDALMDArrayHS(ret);
   11542             : }
   11543             : 
   11544             : /************************************************************************/
   11545             : /*                         GDALGroupDeleteMDArray()                     */
   11546             : /************************************************************************/
   11547             : 
   11548             : /** Delete an array from a group.
   11549             :  *
   11550             :  * After this call, if a previously obtained instance of the deleted object
   11551             :  * is still alive, no method other than for freeing it should be invoked.
   11552             :  *
   11553             :  * This is the same as the C++ method GDALGroup::DeleteMDArray().
   11554             :  *
   11555             :  * @return true in case of success.
   11556             :  * @since GDAL 3.8
   11557             :  */
   11558          20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
   11559             :                             CSLConstList papszOptions)
   11560             : {
   11561          20 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11562          20 :     VALIDATE_POINTER1(pszName, __func__, false);
   11563          20 :     return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
   11564             : }
   11565             : 
   11566             : /************************************************************************/
   11567             : /*                      GDALGroupCreateAttribute()                      */
   11568             : /************************************************************************/
   11569             : 
   11570             : /** Create a attribute within a group.
   11571             :  *
   11572             :  * This is the same as the C++ method GDALGroup::CreateAttribute().
   11573             :  *
   11574             :  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
   11575             :  */
   11576         120 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
   11577             :                                         size_t nDimensions,
   11578             :                                         const GUInt64 *panDimensions,
   11579             :                                         GDALExtendedDataTypeH hEDT,
   11580             :                                         CSLConstList papszOptions)
   11581             : {
   11582         120 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11583         120 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11584         240 :     std::vector<GUInt64> dims;
   11585         120 :     dims.reserve(nDimensions);
   11586         166 :     for (size_t i = 0; i < nDimensions; i++)
   11587          46 :         dims.push_back(panDimensions[i]);
   11588         120 :     auto ret = hGroup->m_poImpl->CreateAttribute(
   11589         360 :         std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
   11590         120 :     if (!ret)
   11591          14 :         return nullptr;
   11592         106 :     return new GDALAttributeHS(ret);
   11593             : }
   11594             : 
   11595             : /************************************************************************/
   11596             : /*                         GDALGroupDeleteAttribute()                   */
   11597             : /************************************************************************/
   11598             : 
   11599             : /** Delete an attribute from a group.
   11600             :  *
   11601             :  * After this call, if a previously obtained instance of the deleted object
   11602             :  * is still alive, no method other than for freeing it should be invoked.
   11603             :  *
   11604             :  * This is the same as the C++ method GDALGroup::DeleteAttribute().
   11605             :  *
   11606             :  * @return true in case of success.
   11607             :  * @since GDAL 3.8
   11608             :  */
   11609          25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
   11610             :                               CSLConstList papszOptions)
   11611             : {
   11612          25 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11613          25 :     VALIDATE_POINTER1(pszName, __func__, false);
   11614          50 :     return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
   11615          25 :                                              papszOptions);
   11616             : }
   11617             : 
   11618             : /************************************************************************/
   11619             : /*                          GDALGroupRename()                           */
   11620             : /************************************************************************/
   11621             : 
   11622             : /** Rename the group.
   11623             :  *
   11624             :  * This is not implemented by all drivers.
   11625             :  *
   11626             :  * Drivers known to implement it: MEM, netCDF.
   11627             :  *
   11628             :  * This is the same as the C++ method GDALGroup::Rename()
   11629             :  *
   11630             :  * @return true in case of success
   11631             :  * @since GDAL 3.8
   11632             :  */
   11633          45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
   11634             : {
   11635          45 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11636          45 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   11637          45 :     return hGroup->m_poImpl->Rename(pszNewName);
   11638             : }
   11639             : 
   11640             : /************************************************************************/
   11641             : /*                 GDALGroupSubsetDimensionFromSelection()              */
   11642             : /************************************************************************/
   11643             : 
   11644             : /** Return a virtual group whose one dimension has been subset according to a
   11645             :  * selection.
   11646             :  *
   11647             :  * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
   11648             :  *
   11649             :  * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
   11650             :  */
   11651             : GDALGroupH
   11652          14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
   11653             :                                       const char *pszSelection,
   11654             :                                       CPL_UNUSED CSLConstList papszOptions)
   11655             : {
   11656          14 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11657          14 :     VALIDATE_POINTER1(pszSelection, __func__, nullptr);
   11658          14 :     auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
   11659          42 :         std::string(pszSelection));
   11660          14 :     if (!hNewGroup)
   11661           8 :         return nullptr;
   11662           6 :     return new GDALGroupHS(hNewGroup);
   11663             : }
   11664             : 
   11665             : /************************************************************************/
   11666             : /*                        GDALMDArrayRelease()                          */
   11667             : /************************************************************************/
   11668             : 
   11669             : /** Release the GDAL in-memory object associated with a GDALMDArray.
   11670             :  *
   11671             :  * Note: when applied on a object coming from a driver, this does not
   11672             :  * destroy the object in the file, database, etc...
   11673             :  */
   11674        1982 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
   11675             : {
   11676        1982 :     delete hMDArray;
   11677        1982 : }
   11678             : 
   11679             : /************************************************************************/
   11680             : /*                        GDALMDArrayGetName()                          */
   11681             : /************************************************************************/
   11682             : 
   11683             : /** Return array name.
   11684             :  *
   11685             :  * This is the same as the C++ method GDALMDArray::GetName()
   11686             :  */
   11687          83 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
   11688             : {
   11689          83 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11690          83 :     return hArray->m_poImpl->GetName().c_str();
   11691             : }
   11692             : 
   11693             : /************************************************************************/
   11694             : /*                    GDALMDArrayGetFullName()                          */
   11695             : /************************************************************************/
   11696             : 
   11697             : /** Return array full name.
   11698             :  *
   11699             :  * This is the same as the C++ method GDALMDArray::GetFullName()
   11700             :  */
   11701          50 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
   11702             : {
   11703          50 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11704          50 :     return hArray->m_poImpl->GetFullName().c_str();
   11705             : }
   11706             : 
   11707             : /************************************************************************/
   11708             : /*                        GDALMDArrayGetName()                          */
   11709             : /************************************************************************/
   11710             : 
   11711             : /** Return the total number of values in the array.
   11712             :  *
   11713             :  * This is the same as the C++ method
   11714             :  * GDALAbstractMDArray::GetTotalElementsCount()
   11715             :  */
   11716           6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
   11717             : {
   11718           6 :     VALIDATE_POINTER1(hArray, __func__, 0);
   11719           6 :     return hArray->m_poImpl->GetTotalElementsCount();
   11720             : }
   11721             : 
   11722             : /************************************************************************/
   11723             : /*                        GDALMDArrayGetDimensionCount()                */
   11724             : /************************************************************************/
   11725             : 
   11726             : /** Return the number of dimensions.
   11727             :  *
   11728             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
   11729             :  */
   11730       10224 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
   11731             : {
   11732       10224 :     VALIDATE_POINTER1(hArray, __func__, 0);
   11733       10224 :     return hArray->m_poImpl->GetDimensionCount();
   11734             : }
   11735             : 
   11736             : /************************************************************************/
   11737             : /*                        GDALMDArrayGetDimensions()                    */
   11738             : /************************************************************************/
   11739             : 
   11740             : /** Return the dimensions of the array
   11741             :  *
   11742             :  * The returned array must be freed with GDALReleaseDimensions(). If only the
   11743             :  * array itself needs to be freed, CPLFree() should be called (and
   11744             :  * GDALDimensionRelease() on individual array members).
   11745             :  *
   11746             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
   11747             :  *
   11748             :  * @param hArray Array.
   11749             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11750             :  *
   11751             :  * @return an array of *pnCount dimensions.
   11752             :  */
   11753        2270 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
   11754             : {
   11755        2270 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11756        2270 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11757        2270 :     const auto &dims(hArray->m_poImpl->GetDimensions());
   11758             :     auto ret = static_cast<GDALDimensionH *>(
   11759        2270 :         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
   11760        6409 :     for (size_t i = 0; i < dims.size(); i++)
   11761             :     {
   11762        4139 :         ret[i] = new GDALDimensionHS(dims[i]);
   11763             :     }
   11764        2270 :     *pnCount = dims.size();
   11765        2270 :     return ret;
   11766             : }
   11767             : 
   11768             : /************************************************************************/
   11769             : /*                        GDALReleaseDimensions()                       */
   11770             : /************************************************************************/
   11771             : 
   11772             : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
   11773             :  *
   11774             :  * @param dims return pointer of above methods
   11775             :  * @param nCount *pnCount value returned by above methods
   11776             :  */
   11777        2343 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
   11778             : {
   11779        6639 :     for (size_t i = 0; i < nCount; i++)
   11780             :     {
   11781        4296 :         delete dims[i];
   11782             :     }
   11783        2343 :     CPLFree(dims);
   11784        2343 : }
   11785             : 
   11786             : /************************************************************************/
   11787             : /*                        GDALMDArrayGetDataType()                     */
   11788             : /************************************************************************/
   11789             : 
   11790             : /** Return the data type
   11791             :  *
   11792             :  * The return must be freed with GDALExtendedDataTypeRelease().
   11793             :  */
   11794        3850 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
   11795             : {
   11796        3850 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11797             :     return new GDALExtendedDataTypeHS(
   11798        3850 :         new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
   11799             : }
   11800             : 
   11801             : /************************************************************************/
   11802             : /*                          GDALMDArrayRead()                           */
   11803             : /************************************************************************/
   11804             : 
   11805             : /** Read part or totality of a multidimensional array.
   11806             :  *
   11807             :  * This is the same as the C++ method GDALAbstractMDArray::Read()
   11808             :  *
   11809             :  * @return TRUE in case of success.
   11810             :  */
   11811        1927 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   11812             :                     const size_t *count, const GInt64 *arrayStep,
   11813             :                     const GPtrDiff_t *bufferStride,
   11814             :                     GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
   11815             :                     const void *pDstBufferAllocStart,
   11816             :                     size_t nDstBufferAllocSize)
   11817             : {
   11818        1927 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11819        1927 :     if ((arrayStartIdx == nullptr || count == nullptr) &&
   11820           0 :         hArray->m_poImpl->GetDimensionCount() > 0)
   11821             :     {
   11822           0 :         VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
   11823           0 :         VALIDATE_POINTER1(count, __func__, FALSE);
   11824             :     }
   11825        1927 :     VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
   11826        1927 :     VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
   11827             :     // coverity[var_deref_model]
   11828        3854 :     return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
   11829        1927 :                                   *(bufferDataType->m_poImpl), pDstBuffer,
   11830        1927 :                                   pDstBufferAllocStart, nDstBufferAllocSize);
   11831             : }
   11832             : 
   11833             : /************************************************************************/
   11834             : /*                          GDALMDArrayWrite()                           */
   11835             : /************************************************************************/
   11836             : 
   11837             : /** Write part or totality of a multidimensional array.
   11838             :  *
   11839             :  * This is the same as the C++ method GDALAbstractMDArray::Write()
   11840             :  *
   11841             :  * @return TRUE in case of success.
   11842             :  */
   11843         558 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   11844             :                      const size_t *count, const GInt64 *arrayStep,
   11845             :                      const GPtrDiff_t *bufferStride,
   11846             :                      GDALExtendedDataTypeH bufferDataType,
   11847             :                      const void *pSrcBuffer, const void *pSrcBufferAllocStart,
   11848             :                      size_t nSrcBufferAllocSize)
   11849             : {
   11850         558 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11851         558 :     if ((arrayStartIdx == nullptr || count == nullptr) &&
   11852           0 :         hArray->m_poImpl->GetDimensionCount() > 0)
   11853             :     {
   11854           0 :         VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
   11855           0 :         VALIDATE_POINTER1(count, __func__, FALSE);
   11856             :     }
   11857         558 :     VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
   11858         558 :     VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
   11859             :     // coverity[var_deref_model]
   11860        1116 :     return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
   11861         558 :                                    bufferStride, *(bufferDataType->m_poImpl),
   11862             :                                    pSrcBuffer, pSrcBufferAllocStart,
   11863         558 :                                    nSrcBufferAllocSize);
   11864             : }
   11865             : 
   11866             : /************************************************************************/
   11867             : /*                       GDALMDArrayAdviseRead()                        */
   11868             : /************************************************************************/
   11869             : 
   11870             : /** Advise driver of upcoming read requests.
   11871             :  *
   11872             :  * This is the same as the C++ method GDALMDArray::AdviseRead()
   11873             :  *
   11874             :  * @return TRUE in case of success.
   11875             :  *
   11876             :  * @since GDAL 3.2
   11877             :  */
   11878           0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   11879             :                           const size_t *count)
   11880             : {
   11881           0 :     return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
   11882             : }
   11883             : 
   11884             : /************************************************************************/
   11885             : /*                      GDALMDArrayAdviseReadEx()                       */
   11886             : /************************************************************************/
   11887             : 
   11888             : /** Advise driver of upcoming read requests.
   11889             :  *
   11890             :  * This is the same as the C++ method GDALMDArray::AdviseRead()
   11891             :  *
   11892             :  * @return TRUE in case of success.
   11893             :  *
   11894             :  * @since GDAL 3.4
   11895             :  */
   11896          22 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   11897             :                             const size_t *count, CSLConstList papszOptions)
   11898             : {
   11899          22 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11900             :     // coverity[var_deref_model]
   11901          22 :     return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
   11902             : }
   11903             : 
   11904             : /************************************************************************/
   11905             : /*                         GDALMDArrayGetAttribute()                    */
   11906             : /************************************************************************/
   11907             : 
   11908             : /** Return an attribute by its name.
   11909             :  *
   11910             :  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
   11911             :  *
   11912             :  * The returned attribute must be freed with GDALAttributeRelease().
   11913             :  */
   11914         119 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
   11915             : {
   11916         119 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11917         119 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11918         357 :     auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
   11919         119 :     if (attr)
   11920         110 :         return new GDALAttributeHS(attr);
   11921           9 :     return nullptr;
   11922             : }
   11923             : 
   11924             : /************************************************************************/
   11925             : /*                        GDALMDArrayGetAttributes()                    */
   11926             : /************************************************************************/
   11927             : 
   11928             : /** Return the list of attributes contained in this array.
   11929             :  *
   11930             :  * The returned array must be freed with GDALReleaseAttributes(). If only the
   11931             :  * array itself needs to be freed, CPLFree() should be called (and
   11932             :  * GDALAttributeRelease() on individual array members).
   11933             :  *
   11934             :  * This is the same as the C++ method GDALMDArray::GetAttributes().
   11935             :  *
   11936             :  * @param hArray Array.
   11937             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11938             :  * @param papszOptions Driver specific options determining how attributes
   11939             :  * should be retrieved. Pass nullptr for default behavior.
   11940             :  *
   11941             :  * @return an array of *pnCount attributes.
   11942             :  */
   11943          58 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
   11944             :                                          CSLConstList papszOptions)
   11945             : {
   11946          58 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11947          58 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11948          58 :     auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
   11949             :     auto ret = static_cast<GDALAttributeH *>(
   11950          58 :         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
   11951         187 :     for (size_t i = 0; i < attrs.size(); i++)
   11952             :     {
   11953         129 :         ret[i] = new GDALAttributeHS(attrs[i]);
   11954             :     }
   11955          58 :     *pnCount = attrs.size();
   11956          58 :     return ret;
   11957             : }
   11958             : 
   11959             : /************************************************************************/
   11960             : /*                       GDALMDArrayCreateAttribute()                   */
   11961             : /************************************************************************/
   11962             : 
   11963             : /** Create a attribute within an array.
   11964             :  *
   11965             :  * This is the same as the C++ method GDALMDArray::CreateAttribute().
   11966             :  *
   11967             :  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
   11968             :  */
   11969         150 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
   11970             :                                           const char *pszName,
   11971             :                                           size_t nDimensions,
   11972             :                                           const GUInt64 *panDimensions,
   11973             :                                           GDALExtendedDataTypeH hEDT,
   11974             :                                           CSLConstList papszOptions)
   11975             : {
   11976         150 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11977         150 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11978         150 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11979         300 :     std::vector<GUInt64> dims;
   11980         150 :     dims.reserve(nDimensions);
   11981         175 :     for (size_t i = 0; i < nDimensions; i++)
   11982          25 :         dims.push_back(panDimensions[i]);
   11983         150 :     auto ret = hArray->m_poImpl->CreateAttribute(
   11984         450 :         std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
   11985         150 :     if (!ret)
   11986           9 :         return nullptr;
   11987         141 :     return new GDALAttributeHS(ret);
   11988             : }
   11989             : 
   11990             : /************************************************************************/
   11991             : /*                       GDALMDArrayDeleteAttribute()                   */
   11992             : /************************************************************************/
   11993             : 
   11994             : /** Delete an attribute from an array.
   11995             :  *
   11996             :  * After this call, if a previously obtained instance of the deleted object
   11997             :  * is still alive, no method other than for freeing it should be invoked.
   11998             :  *
   11999             :  * This is the same as the C++ method GDALMDArray::DeleteAttribute().
   12000             :  *
   12001             :  * @return true in case of success.
   12002             :  * @since GDAL 3.8
   12003             :  */
   12004          24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
   12005             :                                 CSLConstList papszOptions)
   12006             : {
   12007          24 :     VALIDATE_POINTER1(hArray, __func__, false);
   12008          24 :     VALIDATE_POINTER1(pszName, __func__, false);
   12009          48 :     return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
   12010          24 :                                              papszOptions);
   12011             : }
   12012             : 
   12013             : /************************************************************************/
   12014             : /*                       GDALMDArrayGetRawNoDataValue()                 */
   12015             : /************************************************************************/
   12016             : 
   12017             : /** Return the nodata value as a "raw" value.
   12018             :  *
   12019             :  * The value returned might be nullptr in case of no nodata value. When
   12020             :  * a nodata value is registered, a non-nullptr will be returned whose size in
   12021             :  * bytes is GetDataType().GetSize().
   12022             :  *
   12023             :  * The returned value should not be modified or freed.
   12024             :  *
   12025             :  * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
   12026             :  *
   12027             :  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
   12028             :  */
   12029          76 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
   12030             : {
   12031          76 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12032          76 :     return hArray->m_poImpl->GetRawNoDataValue();
   12033             : }
   12034             : 
   12035             : /************************************************************************/
   12036             : /*                      GDALMDArrayGetNoDataValueAsDouble()             */
   12037             : /************************************************************************/
   12038             : 
   12039             : /** Return the nodata value as a double.
   12040             :  *
   12041             :  * The value returned might be nullptr in case of no nodata value. When
   12042             :  * a nodata value is registered, a non-nullptr will be returned whose size in
   12043             :  * bytes is GetDataType().GetSize().
   12044             :  *
   12045             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
   12046             :  *
   12047             :  * @param hArray Array handle.
   12048             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12049             :  * if a nodata value exists and can be converted to double. Might be nullptr.
   12050             :  *
   12051             :  * @return the nodata value as a double. A 0.0 value might also indicate the
   12052             :  * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
   12053             :  * will be set to false then).
   12054             :  */
   12055         119 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
   12056             :                                          int *pbHasNoDataValue)
   12057             : {
   12058         119 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12059         119 :     bool bHasNodataValue = false;
   12060         119 :     double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
   12061         119 :     if (pbHasNoDataValue)
   12062         119 :         *pbHasNoDataValue = bHasNodataValue;
   12063         119 :     return ret;
   12064             : }
   12065             : 
   12066             : /************************************************************************/
   12067             : /*                      GDALMDArrayGetNoDataValueAsInt64()              */
   12068             : /************************************************************************/
   12069             : 
   12070             : /** Return the nodata value as a Int64.
   12071             :  *
   12072             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
   12073             :  *
   12074             :  * @param hArray Array handle.
   12075             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12076             :  * if a nodata value exists and can be converted to Int64. Might be nullptr.
   12077             :  *
   12078             :  * @return the nodata value as a Int64.
   12079             :  * @since GDAL 3.5
   12080             :  */
   12081          11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
   12082             :                                          int *pbHasNoDataValue)
   12083             : {
   12084          11 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12085          11 :     bool bHasNodataValue = false;
   12086          11 :     const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
   12087          11 :     if (pbHasNoDataValue)
   12088          11 :         *pbHasNoDataValue = bHasNodataValue;
   12089          11 :     return ret;
   12090             : }
   12091             : 
   12092             : /************************************************************************/
   12093             : /*                      GDALMDArrayGetNoDataValueAsUInt64()              */
   12094             : /************************************************************************/
   12095             : 
   12096             : /** Return the nodata value as a UInt64.
   12097             :  *
   12098             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
   12099             :  *
   12100             :  * @param hArray Array handle.
   12101             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12102             :  * if a nodata value exists and can be converted to UInt64. Might be nullptr.
   12103             :  *
   12104             :  * @return the nodata value as a UInt64.
   12105             :  * @since GDAL 3.5
   12106             :  */
   12107           7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
   12108             :                                            int *pbHasNoDataValue)
   12109             : {
   12110           7 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12111           7 :     bool bHasNodataValue = false;
   12112           7 :     const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
   12113           7 :     if (pbHasNoDataValue)
   12114           7 :         *pbHasNoDataValue = bHasNodataValue;
   12115           7 :     return ret;
   12116             : }
   12117             : 
   12118             : /************************************************************************/
   12119             : /*                     GDALMDArraySetRawNoDataValue()                   */
   12120             : /************************************************************************/
   12121             : 
   12122             : /** Set the nodata value as a "raw" value.
   12123             :  *
   12124             :  * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
   12125             :  * void*).
   12126             :  *
   12127             :  * @return TRUE in case of success.
   12128             :  */
   12129          14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
   12130             : {
   12131          14 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12132          14 :     return hArray->m_poImpl->SetRawNoDataValue(pNoData);
   12133             : }
   12134             : 
   12135             : /************************************************************************/
   12136             : /*                   GDALMDArraySetNoDataValueAsDouble()                */
   12137             : /************************************************************************/
   12138             : 
   12139             : /** Set the nodata value as a double.
   12140             :  *
   12141             :  * If the natural data type of the attribute/array is not double, type
   12142             :  * conversion will occur to the type returned by GetDataType().
   12143             :  *
   12144             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
   12145             :  *
   12146             :  * @return TRUE in case of success.
   12147             :  */
   12148          51 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
   12149             : {
   12150          51 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12151          51 :     return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
   12152             : }
   12153             : 
   12154             : /************************************************************************/
   12155             : /*                   GDALMDArraySetNoDataValueAsInt64()                 */
   12156             : /************************************************************************/
   12157             : 
   12158             : /** Set the nodata value as a Int64.
   12159             :  *
   12160             :  * If the natural data type of the attribute/array is not Int64, type conversion
   12161             :  * will occur to the type returned by GetDataType().
   12162             :  *
   12163             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
   12164             :  *
   12165             :  * @return TRUE in case of success.
   12166             :  * @since GDAL 3.5
   12167             :  */
   12168           1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
   12169             : {
   12170           1 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12171           1 :     return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
   12172             : }
   12173             : 
   12174             : /************************************************************************/
   12175             : /*                   GDALMDArraySetNoDataValueAsUInt64()                */
   12176             : /************************************************************************/
   12177             : 
   12178             : /** Set the nodata value as a UInt64.
   12179             :  *
   12180             :  * If the natural data type of the attribute/array is not UInt64, type
   12181             :  * conversion will occur to the type returned by GetDataType().
   12182             :  *
   12183             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
   12184             :  *
   12185             :  * @return TRUE in case of success.
   12186             :  * @since GDAL 3.5
   12187             :  */
   12188           1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
   12189             :                                       uint64_t nNoDataValue)
   12190             : {
   12191           1 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12192           1 :     return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
   12193             : }
   12194             : 
   12195             : /************************************************************************/
   12196             : /*                        GDALMDArrayResize()                           */
   12197             : /************************************************************************/
   12198             : 
   12199             : /** Resize an array to new dimensions.
   12200             :  *
   12201             :  * Not all drivers may allow this operation, and with restrictions (e.g.
   12202             :  * for netCDF, this is limited to growing of "unlimited" dimensions)
   12203             :  *
   12204             :  * Resizing a dimension used in other arrays will cause those other arrays
   12205             :  * to be resized.
   12206             :  *
   12207             :  * This is the same as the C++ method GDALMDArray::Resize().
   12208             :  *
   12209             :  * @param hArray Array.
   12210             :  * @param panNewDimSizes Array of GetDimensionCount() values containing the
   12211             :  *                       new size of each indexing dimension.
   12212             :  * @param papszOptions Options. (Driver specific)
   12213             :  * @return true in case of success.
   12214             :  * @since GDAL 3.7
   12215             :  */
   12216          42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
   12217             :                        CSLConstList papszOptions)
   12218             : {
   12219          42 :     VALIDATE_POINTER1(hArray, __func__, false);
   12220          42 :     VALIDATE_POINTER1(panNewDimSizes, __func__, false);
   12221          84 :     std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
   12222         125 :     for (size_t i = 0; i < anNewDimSizes.size(); ++i)
   12223             :     {
   12224          83 :         anNewDimSizes[i] = panNewDimSizes[i];
   12225             :     }
   12226          42 :     return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
   12227             : }
   12228             : 
   12229             : /************************************************************************/
   12230             : /*                          GDALMDArraySetScale()                       */
   12231             : /************************************************************************/
   12232             : 
   12233             : /** Set the scale value to apply to raw values.
   12234             :  *
   12235             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12236             :  *
   12237             :  * This is the same as the C++ method GDALMDArray::SetScale().
   12238             :  *
   12239             :  * @return TRUE in case of success.
   12240             :  */
   12241           0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
   12242             : {
   12243           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12244           0 :     return hArray->m_poImpl->SetScale(dfScale);
   12245             : }
   12246             : 
   12247             : /************************************************************************/
   12248             : /*                        GDALMDArraySetScaleEx()                       */
   12249             : /************************************************************************/
   12250             : 
   12251             : /** Set the scale value to apply to raw values.
   12252             :  *
   12253             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12254             :  *
   12255             :  * This is the same as the C++ method GDALMDArray::SetScale().
   12256             :  *
   12257             :  * @return TRUE in case of success.
   12258             :  * @since GDAL 3.3
   12259             :  */
   12260          21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
   12261             :                           GDALDataType eStorageType)
   12262             : {
   12263          21 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12264          21 :     return hArray->m_poImpl->SetScale(dfScale, eStorageType);
   12265             : }
   12266             : 
   12267             : /************************************************************************/
   12268             : /*                          GDALMDArraySetOffset()                       */
   12269             : /************************************************************************/
   12270             : 
   12271             : /** Set the scale value to apply to raw values.
   12272             :  *
   12273             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12274             :  *
   12275             :  * This is the same as the C++ method GDALMDArray::SetOffset().
   12276             :  *
   12277             :  * @return TRUE in case of success.
   12278             :  */
   12279           0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
   12280             : {
   12281           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12282           0 :     return hArray->m_poImpl->SetOffset(dfOffset);
   12283             : }
   12284             : 
   12285             : /************************************************************************/
   12286             : /*                       GDALMDArraySetOffsetEx()                       */
   12287             : /************************************************************************/
   12288             : 
   12289             : /** Set the scale value to apply to raw values.
   12290             :  *
   12291             :  * unscaled_value = raw_value * GetOffset() + GetOffset()
   12292             :  *
   12293             :  * This is the same as the C++ method GDALMDArray::SetOffset().
   12294             :  *
   12295             :  * @return TRUE in case of success.
   12296             :  * @since GDAL 3.3
   12297             :  */
   12298          21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
   12299             :                            GDALDataType eStorageType)
   12300             : {
   12301          21 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12302          21 :     return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
   12303             : }
   12304             : 
   12305             : /************************************************************************/
   12306             : /*                          GDALMDArrayGetScale()                       */
   12307             : /************************************************************************/
   12308             : 
   12309             : /** Get the scale value to apply to raw values.
   12310             :  *
   12311             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12312             :  *
   12313             :  * This is the same as the C++ method GDALMDArray::GetScale().
   12314             :  *
   12315             :  * @return the scale value
   12316             :  */
   12317         103 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
   12318             : {
   12319         103 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12320         103 :     bool bHasValue = false;
   12321         103 :     double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
   12322         103 :     if (pbHasValue)
   12323         103 :         *pbHasValue = bHasValue;
   12324         103 :     return dfRet;
   12325             : }
   12326             : 
   12327             : /************************************************************************/
   12328             : /*                        GDALMDArrayGetScaleEx()                       */
   12329             : /************************************************************************/
   12330             : 
   12331             : /** Get the scale value to apply to raw values.
   12332             :  *
   12333             :  * unscaled_value = raw_value * GetScale() + GetScale()
   12334             :  *
   12335             :  * This is the same as the C++ method GDALMDArray::GetScale().
   12336             :  *
   12337             :  * @return the scale value
   12338             :  * @since GDAL 3.3
   12339             :  */
   12340           5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
   12341             :                              GDALDataType *peStorageType)
   12342             : {
   12343           5 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12344           5 :     bool bHasValue = false;
   12345           5 :     double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
   12346           5 :     if (pbHasValue)
   12347           5 :         *pbHasValue = bHasValue;
   12348           5 :     return dfRet;
   12349             : }
   12350             : 
   12351             : /************************************************************************/
   12352             : /*                          GDALMDArrayGetOffset()                      */
   12353             : /************************************************************************/
   12354             : 
   12355             : /** Get the scale value to apply to raw values.
   12356             :  *
   12357             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12358             :  *
   12359             :  * This is the same as the C++ method GDALMDArray::GetOffset().
   12360             :  *
   12361             :  * @return the scale value
   12362             :  */
   12363         100 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
   12364             : {
   12365         100 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12366         100 :     bool bHasValue = false;
   12367         100 :     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
   12368         100 :     if (pbHasValue)
   12369         100 :         *pbHasValue = bHasValue;
   12370         100 :     return dfRet;
   12371             : }
   12372             : 
   12373             : /************************************************************************/
   12374             : /*                        GDALMDArrayGetOffsetEx()                      */
   12375             : /************************************************************************/
   12376             : 
   12377             : /** Get the scale value to apply to raw values.
   12378             :  *
   12379             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12380             :  *
   12381             :  * This is the same as the C++ method GDALMDArray::GetOffset().
   12382             :  *
   12383             :  * @return the scale value
   12384             :  * @since GDAL 3.3
   12385             :  */
   12386           5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
   12387             :                               GDALDataType *peStorageType)
   12388             : {
   12389           5 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12390           5 :     bool bHasValue = false;
   12391           5 :     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
   12392           5 :     if (pbHasValue)
   12393           5 :         *pbHasValue = bHasValue;
   12394           5 :     return dfRet;
   12395             : }
   12396             : 
   12397             : /************************************************************************/
   12398             : /*                      GDALMDArrayGetBlockSize()                       */
   12399             : /************************************************************************/
   12400             : 
   12401             : /** Return the "natural" block size of the array along all dimensions.
   12402             :  *
   12403             :  * Some drivers might organize the array in tiles/blocks and reading/writing
   12404             :  * aligned on those tile/block boundaries will be more efficient.
   12405             :  *
   12406             :  * The returned number of elements in the vector is the same as
   12407             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
   12408             :  * the natural block size along the considered dimension.
   12409             :  * "Flat" arrays will typically return a vector of values set to 0.
   12410             :  *
   12411             :  * The default implementation will return a vector of values set to 0.
   12412             :  *
   12413             :  * This method is used by GetProcessingChunkSize().
   12414             :  *
   12415             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
   12416             :  * theoretical case of a 32-bit platform, this might exceed its size_t
   12417             :  * allocation capabilities.
   12418             :  *
   12419             :  * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
   12420             :  *
   12421             :  * @return the block size, in number of elements along each dimension.
   12422             :  */
   12423          93 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
   12424             : {
   12425          93 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12426          93 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12427          93 :     auto res = hArray->m_poImpl->GetBlockSize();
   12428          93 :     auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
   12429         285 :     for (size_t i = 0; i < res.size(); i++)
   12430             :     {
   12431         192 :         ret[i] = res[i];
   12432             :     }
   12433          93 :     *pnCount = res.size();
   12434          93 :     return ret;
   12435             : }
   12436             : 
   12437             : /***********************************************************************/
   12438             : /*                   GDALMDArrayGetProcessingChunkSize()               */
   12439             : /************************************************************************/
   12440             : 
   12441             : /** \brief Return an optimal chunk size for read/write operations, given the
   12442             :  * natural block size and memory constraints specified.
   12443             :  *
   12444             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
   12445             :  * multiple of those returned by GetBlockSize() (unless the block define by
   12446             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
   12447             :  * returned by this method).
   12448             :  *
   12449             :  * This is the same as the C++ method
   12450             :  * GDALAbstractMDArray::GetProcessingChunkSize().
   12451             :  *
   12452             :  * @param hArray Array.
   12453             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12454             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
   12455             :  * chunk.
   12456             :  *
   12457             :  * @return the chunk size, in number of elements along each dimension.
   12458             :  */
   12459             : 
   12460           1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
   12461             :                                           size_t nMaxChunkMemory)
   12462             : {
   12463           1 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12464           1 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12465           1 :     auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
   12466           1 :     auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
   12467           3 :     for (size_t i = 0; i < res.size(); i++)
   12468             :     {
   12469           2 :         ret[i] = res[i];
   12470             :     }
   12471           1 :     *pnCount = res.size();
   12472           1 :     return ret;
   12473             : }
   12474             : 
   12475             : /************************************************************************/
   12476             : /*                     GDALMDArrayGetStructuralInfo()                   */
   12477             : /************************************************************************/
   12478             : 
   12479             : /** Return structural information on the array.
   12480             :  *
   12481             :  * This may be the compression, etc..
   12482             :  *
   12483             :  * The return value should not be freed and is valid until GDALMDArray is
   12484             :  * released or this function called again.
   12485             :  *
   12486             :  * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
   12487             :  */
   12488          15 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
   12489             : {
   12490          15 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12491          15 :     return hArray->m_poImpl->GetStructuralInfo();
   12492             : }
   12493             : 
   12494             : /************************************************************************/
   12495             : /*                        GDALMDArrayGetView()                          */
   12496             : /************************************************************************/
   12497             : 
   12498             : /** Return a view of the array using slicing or field access.
   12499             :  *
   12500             :  * The returned object should be released with GDALMDArrayRelease().
   12501             :  *
   12502             :  * This is the same as the C++ method GDALMDArray::GetView().
   12503             :  */
   12504         430 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
   12505             : {
   12506         430 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12507         430 :     VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
   12508        1290 :     auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
   12509         430 :     if (!sliced)
   12510          22 :         return nullptr;
   12511         408 :     return new GDALMDArrayHS(sliced);
   12512             : }
   12513             : 
   12514             : /************************************************************************/
   12515             : /*                       GDALMDArrayTranspose()                         */
   12516             : /************************************************************************/
   12517             : 
   12518             : /** Return a view of the array whose axis have been reordered.
   12519             :  *
   12520             :  * The returned object should be released with GDALMDArrayRelease().
   12521             :  *
   12522             :  * This is the same as the C++ method GDALMDArray::Transpose().
   12523             :  */
   12524          44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
   12525             :                                   const int *panMapNewAxisToOldAxis)
   12526             : {
   12527          44 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12528          88 :     std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
   12529          44 :     if (nNewAxisCount)
   12530             :     {
   12531          43 :         memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
   12532             :                nNewAxisCount * sizeof(int));
   12533             :     }
   12534          88 :     auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
   12535          44 :     if (!reordered)
   12536           7 :         return nullptr;
   12537          37 :     return new GDALMDArrayHS(reordered);
   12538             : }
   12539             : 
   12540             : /************************************************************************/
   12541             : /*                      GDALMDArrayGetUnscaled()                        */
   12542             : /************************************************************************/
   12543             : 
   12544             : /** Return an array that is the unscaled version of the current one.
   12545             :  *
   12546             :  * That is each value of the unscaled array will be
   12547             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12548             :  *
   12549             :  * Starting with GDAL 3.3, the Write() method is implemented and will convert
   12550             :  * from unscaled values to raw values.
   12551             :  *
   12552             :  * The returned object should be released with GDALMDArrayRelease().
   12553             :  *
   12554             :  * This is the same as the C++ method GDALMDArray::GetUnscaled().
   12555             :  */
   12556          13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
   12557             : {
   12558          13 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12559          26 :     auto unscaled = hArray->m_poImpl->GetUnscaled();
   12560          13 :     if (!unscaled)
   12561           0 :         return nullptr;
   12562          13 :     return new GDALMDArrayHS(unscaled);
   12563             : }
   12564             : 
   12565             : /************************************************************************/
   12566             : /*                          GDALMDArrayGetMask()                         */
   12567             : /************************************************************************/
   12568             : 
   12569             : /** Return an array that is a mask for the current array
   12570             :  *
   12571             :  * This array will be of type Byte, with values set to 0 to indicate invalid
   12572             :  * pixels of the current array, and values set to 1 to indicate valid pixels.
   12573             :  *
   12574             :  * The returned object should be released with GDALMDArrayRelease().
   12575             :  *
   12576             :  * This is the same as the C++ method GDALMDArray::GetMask().
   12577             :  */
   12578          35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
   12579             : {
   12580          35 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12581          70 :     auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
   12582          35 :     if (!unscaled)
   12583           7 :         return nullptr;
   12584          28 :     return new GDALMDArrayHS(unscaled);
   12585             : }
   12586             : 
   12587             : /************************************************************************/
   12588             : /*                   GDALMDArrayGetResampled()                          */
   12589             : /************************************************************************/
   12590             : 
   12591             : /** Return an array that is a resampled / reprojected view of the current array
   12592             :  *
   12593             :  * This is the same as the C++ method GDALMDArray::GetResampled().
   12594             :  *
   12595             :  * Currently this method can only resample along the last 2 dimensions, unless
   12596             :  * orthorectifying a NASA EMIT dataset.
   12597             :  *
   12598             :  * The returned object should be released with GDALMDArrayRelease().
   12599             :  *
   12600             :  * @since 3.4
   12601             :  */
   12602          34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
   12603             :                                      const GDALDimensionH *pahNewDims,
   12604             :                                      GDALRIOResampleAlg resampleAlg,
   12605             :                                      OGRSpatialReferenceH hTargetSRS,
   12606             :                                      CSLConstList papszOptions)
   12607             : {
   12608          34 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12609          34 :     VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
   12610          68 :     std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
   12611         112 :     for (size_t i = 0; i < nNewDimCount; ++i)
   12612             :     {
   12613          78 :         if (pahNewDims[i])
   12614           8 :             apoNewDims[i] = pahNewDims[i]->m_poImpl;
   12615             :     }
   12616          34 :     auto poNewArray = hArray->m_poImpl->GetResampled(
   12617          34 :         apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
   12618          68 :         papszOptions);
   12619          34 :     if (!poNewArray)
   12620           8 :         return nullptr;
   12621          26 :     return new GDALMDArrayHS(poNewArray);
   12622             : }
   12623             : 
   12624             : /************************************************************************/
   12625             : /*                      GDALMDArraySetUnit()                            */
   12626             : /************************************************************************/
   12627             : 
   12628             : /** Set the variable unit.
   12629             :  *
   12630             :  * Values should conform as much as possible with those allowed by
   12631             :  * the NetCDF CF conventions:
   12632             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
   12633             :  * but others might be returned.
   12634             :  *
   12635             :  * Few examples are "meter", "degrees", "second", ...
   12636             :  * Empty value means unknown.
   12637             :  *
   12638             :  * This is the same as the C function GDALMDArraySetUnit()
   12639             :  *
   12640             :  * @param hArray array.
   12641             :  * @param pszUnit unit name.
   12642             :  * @return TRUE in case of success.
   12643             :  */
   12644          15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
   12645             : {
   12646          15 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12647          15 :     return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
   12648             : }
   12649             : 
   12650             : /************************************************************************/
   12651             : /*                      GDALMDArrayGetUnit()                            */
   12652             : /************************************************************************/
   12653             : 
   12654             : /** Return the array unit.
   12655             :  *
   12656             :  * Values should conform as much as possible with those allowed by
   12657             :  * the NetCDF CF conventions:
   12658             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
   12659             :  * but others might be returned.
   12660             :  *
   12661             :  * Few examples are "meter", "degrees", "second", ...
   12662             :  * Empty value means unknown.
   12663             :  *
   12664             :  * The return value should not be freed and is valid until GDALMDArray is
   12665             :  * released or this function called again.
   12666             :  *
   12667             :  * This is the same as the C++ method GDALMDArray::GetUnit().
   12668             :  */
   12669         111 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
   12670             : {
   12671         111 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12672         111 :     return hArray->m_poImpl->GetUnit().c_str();
   12673             : }
   12674             : 
   12675             : /************************************************************************/
   12676             : /*                      GDALMDArrayGetSpatialRef()                      */
   12677             : /************************************************************************/
   12678             : 
   12679             : /** Assign a spatial reference system object to the array.
   12680             :  *
   12681             :  * This is the same as the C++ method GDALMDArray::SetSpatialRef().
   12682             :  * @return TRUE in case of success.
   12683             :  */
   12684          30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
   12685             : {
   12686          30 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12687          60 :     return hArray->m_poImpl->SetSpatialRef(
   12688          60 :         OGRSpatialReference::FromHandle(hSRS));
   12689             : }
   12690             : 
   12691             : /************************************************************************/
   12692             : /*                      GDALMDArrayGetSpatialRef()                      */
   12693             : /************************************************************************/
   12694             : 
   12695             : /** Return the spatial reference system object associated with the array.
   12696             :  *
   12697             :  * This is the same as the C++ method GDALMDArray::GetSpatialRef().
   12698             :  *
   12699             :  * The returned object must be freed with OSRDestroySpatialReference().
   12700             :  */
   12701          77 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
   12702             : {
   12703          77 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12704          77 :     auto poSRS = hArray->m_poImpl->GetSpatialRef();
   12705          77 :     return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
   12706             : }
   12707             : 
   12708             : /************************************************************************/
   12709             : /*                      GDALMDArrayGetStatistics()                      */
   12710             : /************************************************************************/
   12711             : 
   12712             : /**
   12713             :  * \brief Fetch statistics.
   12714             :  *
   12715             :  * This is the same as the C++ method GDALMDArray::GetStatistics().
   12716             :  *
   12717             :  * @since GDAL 3.2
   12718             :  */
   12719             : 
   12720          15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
   12721             :                                 int bApproxOK, int bForce, double *pdfMin,
   12722             :                                 double *pdfMax, double *pdfMean,
   12723             :                                 double *pdfStdDev, GUInt64 *pnValidCount,
   12724             :                                 GDALProgressFunc pfnProgress,
   12725             :                                 void *pProgressData)
   12726             : {
   12727          15 :     VALIDATE_POINTER1(hArray, __func__, CE_Failure);
   12728          30 :     return hArray->m_poImpl->GetStatistics(
   12729          15 :         CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
   12730          15 :         pdfStdDev, pnValidCount, pfnProgress, pProgressData);
   12731             : }
   12732             : 
   12733             : /************************************************************************/
   12734             : /*                      GDALMDArrayComputeStatistics()                  */
   12735             : /************************************************************************/
   12736             : 
   12737             : /**
   12738             :  * \brief Compute statistics.
   12739             :  *
   12740             :  * This is the same as the C++ method GDALMDArray::ComputeStatistics().
   12741             :  *
   12742             :  * @since GDAL 3.2
   12743             :  * @see GDALMDArrayComputeStatisticsEx()
   12744             :  */
   12745             : 
   12746           0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
   12747             :                                  int bApproxOK, double *pdfMin, double *pdfMax,
   12748             :                                  double *pdfMean, double *pdfStdDev,
   12749             :                                  GUInt64 *pnValidCount,
   12750             :                                  GDALProgressFunc pfnProgress,
   12751             :                                  void *pProgressData)
   12752             : {
   12753           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12754           0 :     return hArray->m_poImpl->ComputeStatistics(
   12755           0 :         CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
   12756           0 :         pnValidCount, pfnProgress, pProgressData, nullptr);
   12757             : }
   12758             : 
   12759             : /************************************************************************/
   12760             : /*                     GDALMDArrayComputeStatisticsEx()                 */
   12761             : /************************************************************************/
   12762             : 
   12763             : /**
   12764             :  * \brief Compute statistics.
   12765             :  *
   12766             :  * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
   12767             :  *
   12768             :  * This is the same as the C++ method GDALMDArray::ComputeStatistics().
   12769             :  *
   12770             :  * @since GDAL 3.8
   12771             :  */
   12772             : 
   12773           4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
   12774             :                                    int bApproxOK, double *pdfMin,
   12775             :                                    double *pdfMax, double *pdfMean,
   12776             :                                    double *pdfStdDev, GUInt64 *pnValidCount,
   12777             :                                    GDALProgressFunc pfnProgress,
   12778             :                                    void *pProgressData,
   12779             :                                    CSLConstList papszOptions)
   12780             : {
   12781           4 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12782           8 :     return hArray->m_poImpl->ComputeStatistics(
   12783           4 :         CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
   12784           8 :         pnValidCount, pfnProgress, pProgressData, papszOptions);
   12785             : }
   12786             : 
   12787             : /************************************************************************/
   12788             : /*                 GDALMDArrayGetCoordinateVariables()                  */
   12789             : /************************************************************************/
   12790             : 
   12791             : /** Return coordinate variables.
   12792             :  *
   12793             :  * The returned array must be freed with GDALReleaseArrays(). If only the array
   12794             :  * itself needs to be freed, CPLFree() should be called (and
   12795             :  * GDALMDArrayRelease() on individual array members).
   12796             :  *
   12797             :  * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
   12798             :  *
   12799             :  * @param hArray Array.
   12800             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12801             :  *
   12802             :  * @return an array of *pnCount arrays.
   12803             :  * @since 3.4
   12804             :  */
   12805          13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
   12806             :                                                 size_t *pnCount)
   12807             : {
   12808          13 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12809          13 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12810          13 :     const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
   12811             :     auto ret = static_cast<GDALMDArrayH *>(
   12812          13 :         CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
   12813          29 :     for (size_t i = 0; i < coordinates.size(); i++)
   12814             :     {
   12815          16 :         ret[i] = new GDALMDArrayHS(coordinates[i]);
   12816             :     }
   12817          13 :     *pnCount = coordinates.size();
   12818          13 :     return ret;
   12819             : }
   12820             : 
   12821             : /************************************************************************/
   12822             : /*                     GDALMDArrayGetGridded()                          */
   12823             : /************************************************************************/
   12824             : 
   12825             : /** Return a gridded array from scattered point data, that is from an array
   12826             :  * whose last dimension is the indexing variable of X and Y arrays.
   12827             :  *
   12828             :  * The returned object should be released with GDALMDArrayRelease().
   12829             :  *
   12830             :  * This is the same as the C++ method GDALMDArray::GetGridded().
   12831             :  *
   12832             :  * @since GDAL 3.7
   12833             :  */
   12834          22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
   12835             :                                    const char *pszGridOptions,
   12836             :                                    GDALMDArrayH hXArray, GDALMDArrayH hYArray,
   12837             :                                    CSLConstList papszOptions)
   12838             : {
   12839          22 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12840          22 :     VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
   12841          22 :     auto gridded = hArray->m_poImpl->GetGridded(
   12842          44 :         pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
   12843          88 :         hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
   12844          22 :     if (!gridded)
   12845          19 :         return nullptr;
   12846           3 :     return new GDALMDArrayHS(gridded);
   12847             : }
   12848             : 
   12849             : /************************************************************************/
   12850             : /*                      GDALMDArrayGetMeshGrid()                        */
   12851             : /************************************************************************/
   12852             : 
   12853             : /** Return a list of multidimensional arrays from a list of one-dimensional
   12854             :  * arrays.
   12855             :  *
   12856             :  * This is typically used to transform one-dimensional longitude, latitude
   12857             :  * arrays into 2D ones.
   12858             :  *
   12859             :  * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
   12860             :  * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
   12861             :  * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
   12862             :  * repeated to fill the matrix along the first dimension for x1, the second
   12863             :  * for x2 and so on.
   12864             :  *
   12865             :  * For example, if x = [1, 2], and y = [3, 4, 5],
   12866             :  * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
   12867             :  * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
   12868             :  * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
   12869             :  *
   12870             :  * and
   12871             :  * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
   12872             :  * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
   12873             :  * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
   12874             :  *
   12875             :  * The currently supported options are:
   12876             :  * <ul>
   12877             :  * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
   12878             :  * output.
   12879             :  * </li>
   12880             :  * </ul>
   12881             :  *
   12882             :  * This is the same as
   12883             :  * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
   12884             :  * function.
   12885             :  *
   12886             :  * The returned array (of arrays) must be freed with GDALReleaseArrays().
   12887             :  * If only the array itself needs to be freed, CPLFree() should be called
   12888             :  * (and GDALMDArrayRelease() on individual array members).
   12889             :  *
   12890             :  * This is the same as the C++ method GDALMDArray::GetMeshGrid()
   12891             :  *
   12892             :  * @param pahInputArrays Input arrays
   12893             :  * @param nCountInputArrays Number of input arrays
   12894             :  * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
   12895             :  * @param papszOptions NULL, or NULL terminated list of options.
   12896             :  *
   12897             :  * @return an array of *pnCountOutputArrays arrays.
   12898             :  * @since 3.10
   12899             :  */
   12900           7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
   12901             :                                      size_t nCountInputArrays,
   12902             :                                      size_t *pnCountOutputArrays,
   12903             :                                      CSLConstList papszOptions)
   12904             : {
   12905           7 :     VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
   12906           7 :     VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
   12907             : 
   12908          14 :     std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
   12909          20 :     for (size_t i = 0; i < nCountInputArrays; ++i)
   12910          13 :         apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
   12911             : 
   12912             :     const auto apoOutputArrays =
   12913           7 :         GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
   12914             :     auto ret = static_cast<GDALMDArrayH *>(
   12915           7 :         CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
   12916          17 :     for (size_t i = 0; i < apoOutputArrays.size(); i++)
   12917             :     {
   12918          10 :         ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
   12919             :     }
   12920           7 :     *pnCountOutputArrays = apoOutputArrays.size();
   12921           7 :     return ret;
   12922             : }
   12923             : 
   12924             : /************************************************************************/
   12925             : /*                        GDALReleaseArrays()                           */
   12926             : /************************************************************************/
   12927             : 
   12928             : /** Free the return of GDALMDArrayGetCoordinateVariables()
   12929             :  *
   12930             :  * @param arrays return pointer of above methods
   12931             :  * @param nCount *pnCount value returned by above methods
   12932             :  */
   12933          20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
   12934             : {
   12935          46 :     for (size_t i = 0; i < nCount; i++)
   12936             :     {
   12937          26 :         delete arrays[i];
   12938             :     }
   12939          20 :     CPLFree(arrays);
   12940          20 : }
   12941             : 
   12942             : /************************************************************************/
   12943             : /*                           GDALMDArrayCache()                         */
   12944             : /************************************************************************/
   12945             : 
   12946             : /**
   12947             :  * \brief Cache the content of the array into an auxiliary filename.
   12948             :  *
   12949             :  * This is the same as the C++ method GDALMDArray::Cache().
   12950             :  *
   12951             :  * @since GDAL 3.4
   12952             :  */
   12953             : 
   12954           7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
   12955             : {
   12956           7 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12957           7 :     return hArray->m_poImpl->Cache(papszOptions);
   12958             : }
   12959             : 
   12960             : /************************************************************************/
   12961             : /*                       GDALMDArrayRename()                           */
   12962             : /************************************************************************/
   12963             : 
   12964             : /** Rename the array.
   12965             :  *
   12966             :  * This is not implemented by all drivers.
   12967             :  *
   12968             :  * Drivers known to implement it: MEM, netCDF, Zarr.
   12969             :  *
   12970             :  * This is the same as the C++ method GDALAbstractMDArray::Rename()
   12971             :  *
   12972             :  * @return true in case of success
   12973             :  * @since GDAL 3.8
   12974             :  */
   12975          28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
   12976             : {
   12977          28 :     VALIDATE_POINTER1(hArray, __func__, false);
   12978          28 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   12979          28 :     return hArray->m_poImpl->Rename(pszNewName);
   12980             : }
   12981             : 
   12982             : /************************************************************************/
   12983             : /*                        GDALAttributeRelease()                        */
   12984             : /************************************************************************/
   12985             : 
   12986             : /** Release the GDAL in-memory object associated with a GDALAttribute.
   12987             :  *
   12988             :  * Note: when applied on a object coming from a driver, this does not
   12989             :  * destroy the object in the file, database, etc...
   12990             :  */
   12991         720 : void GDALAttributeRelease(GDALAttributeH hAttr)
   12992             : {
   12993         720 :     delete hAttr;
   12994         720 : }
   12995             : 
   12996             : /************************************************************************/
   12997             : /*                        GDALAttributeGetName()                        */
   12998             : /************************************************************************/
   12999             : 
   13000             : /** Return the name of the attribute.
   13001             :  *
   13002             :  * The returned pointer is valid until hAttr is released.
   13003             :  *
   13004             :  * This is the same as the C++ method GDALAttribute::GetName().
   13005             :  */
   13006         361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
   13007             : {
   13008         361 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13009         361 :     return hAttr->m_poImpl->GetName().c_str();
   13010             : }
   13011             : 
   13012             : /************************************************************************/
   13013             : /*                      GDALAttributeGetFullName()                      */
   13014             : /************************************************************************/
   13015             : 
   13016             : /** Return the full name of the attribute.
   13017             :  *
   13018             :  * The returned pointer is valid until hAttr is released.
   13019             :  *
   13020             :  * This is the same as the C++ method GDALAttribute::GetFullName().
   13021             :  */
   13022          49 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
   13023             : {
   13024          49 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13025          49 :     return hAttr->m_poImpl->GetFullName().c_str();
   13026             : }
   13027             : 
   13028             : /************************************************************************/
   13029             : /*                   GDALAttributeGetTotalElementsCount()               */
   13030             : /************************************************************************/
   13031             : 
   13032             : /** Return the total number of values in the attribute.
   13033             :  *
   13034             :  * This is the same as the C++ method
   13035             :  * GDALAbstractMDArray::GetTotalElementsCount()
   13036             :  */
   13037         176 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
   13038             : {
   13039         176 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13040         176 :     return hAttr->m_poImpl->GetTotalElementsCount();
   13041             : }
   13042             : 
   13043             : /************************************************************************/
   13044             : /*                    GDALAttributeGetDimensionCount()                */
   13045             : /************************************************************************/
   13046             : 
   13047             : /** Return the number of dimensions.
   13048             :  *
   13049             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
   13050             :  */
   13051          12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
   13052             : {
   13053          12 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13054          12 :     return hAttr->m_poImpl->GetDimensionCount();
   13055             : }
   13056             : 
   13057             : /************************************************************************/
   13058             : /*                       GDALAttributeGetDimensionsSize()                */
   13059             : /************************************************************************/
   13060             : 
   13061             : /** Return the dimension sizes of the attribute.
   13062             :  *
   13063             :  * The returned array must be freed with CPLFree()
   13064             :  *
   13065             :  * @param hAttr Attribute.
   13066             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13067             :  *
   13068             :  * @return an array of *pnCount values.
   13069             :  */
   13070          11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
   13071             : {
   13072          11 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13073          11 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13074          11 :     const auto &dims = hAttr->m_poImpl->GetDimensions();
   13075          11 :     auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
   13076          22 :     for (size_t i = 0; i < dims.size(); i++)
   13077             :     {
   13078          11 :         ret[i] = dims[i]->GetSize();
   13079             :     }
   13080          11 :     *pnCount = dims.size();
   13081          11 :     return ret;
   13082             : }
   13083             : 
   13084             : /************************************************************************/
   13085             : /*                       GDALAttributeGetDataType()                     */
   13086             : /************************************************************************/
   13087             : 
   13088             : /** Return the data type
   13089             :  *
   13090             :  * The return must be freed with GDALExtendedDataTypeRelease().
   13091             :  */
   13092         427 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
   13093             : {
   13094         427 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13095             :     return new GDALExtendedDataTypeHS(
   13096         427 :         new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
   13097             : }
   13098             : 
   13099             : /************************************************************************/
   13100             : /*                       GDALAttributeReadAsRaw()                       */
   13101             : /************************************************************************/
   13102             : 
   13103             : /** Return the raw value of an attribute.
   13104             :  *
   13105             :  * This is the same as the C++ method GDALAttribute::ReadAsRaw().
   13106             :  *
   13107             :  * The returned buffer must be freed with GDALAttributeFreeRawResult()
   13108             :  *
   13109             :  * @param hAttr Attribute.
   13110             :  * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
   13111             :  *
   13112             :  * @return a buffer of *pnSize bytes.
   13113             :  */
   13114           6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
   13115             : {
   13116           6 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13117           6 :     VALIDATE_POINTER1(pnSize, __func__, nullptr);
   13118          12 :     auto res(hAttr->m_poImpl->ReadAsRaw());
   13119           6 :     *pnSize = res.size();
   13120           6 :     auto ret = res.StealData();
   13121           6 :     if (!ret)
   13122             :     {
   13123           0 :         *pnSize = 0;
   13124           0 :         return nullptr;
   13125             :     }
   13126           6 :     return ret;
   13127             : }
   13128             : 
   13129             : /************************************************************************/
   13130             : /*                       GDALAttributeFreeRawResult()                   */
   13131             : /************************************************************************/
   13132             : 
   13133             : /** Free the return of GDALAttributeAsRaw()
   13134             :  */
   13135           6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
   13136             :                                 CPL_UNUSED size_t nSize)
   13137             : {
   13138           6 :     VALIDATE_POINTER0(hAttr, __func__);
   13139           6 :     if (raw)
   13140             :     {
   13141           6 :         const auto &dt(hAttr->m_poImpl->GetDataType());
   13142           6 :         const auto nDTSize(dt.GetSize());
   13143           6 :         GByte *pabyPtr = raw;
   13144           6 :         const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
   13145           6 :         CPLAssert(nSize == nDTSize * nEltCount);
   13146          12 :         for (size_t i = 0; i < nEltCount; ++i)
   13147             :         {
   13148           6 :             dt.FreeDynamicMemory(pabyPtr);
   13149           6 :             pabyPtr += nDTSize;
   13150             :         }
   13151           6 :         CPLFree(raw);
   13152             :     }
   13153             : }
   13154             : 
   13155             : /************************************************************************/
   13156             : /*                       GDALAttributeReadAsString()                    */
   13157             : /************************************************************************/
   13158             : 
   13159             : /** Return the value of an attribute as a string.
   13160             :  *
   13161             :  * The returned string should not be freed, and its lifetime does not
   13162             :  * excess a next call to ReadAsString() on the same object, or the deletion
   13163             :  * of the object itself.
   13164             :  *
   13165             :  * This function will only return the first element if there are several.
   13166             :  *
   13167             :  * This is the same as the C++ method GDALAttribute::ReadAsString()
   13168             :  *
   13169             :  * @return a string, or nullptr.
   13170             :  */
   13171         107 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
   13172             : {
   13173         107 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13174         107 :     return hAttr->m_poImpl->ReadAsString();
   13175             : }
   13176             : 
   13177             : /************************************************************************/
   13178             : /*                      GDALAttributeReadAsInt()                        */
   13179             : /************************************************************************/
   13180             : 
   13181             : /** Return the value of an attribute as a integer.
   13182             :  *
   13183             :  * This function will only return the first element if there are several.
   13184             :  *
   13185             :  * It can fail if its value can not be converted to integer.
   13186             :  *
   13187             :  * This is the same as the C++ method GDALAttribute::ReadAsInt()
   13188             :  *
   13189             :  * @return a integer, or INT_MIN in case of error.
   13190             :  */
   13191          22 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
   13192             : {
   13193          22 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13194          22 :     return hAttr->m_poImpl->ReadAsInt();
   13195             : }
   13196             : 
   13197             : /************************************************************************/
   13198             : /*                      GDALAttributeReadAsInt64()                      */
   13199             : /************************************************************************/
   13200             : 
   13201             : /** Return the value of an attribute as a int64_t.
   13202             :  *
   13203             :  * This function will only return the first element if there are several.
   13204             :  *
   13205             :  * It can fail if its value can not be converted to integer.
   13206             :  *
   13207             :  * This is the same as the C++ method GDALAttribute::ReadAsInt64()
   13208             :  *
   13209             :  * @return an int64_t, or INT64_MIN in case of error.
   13210             :  */
   13211          15 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
   13212             : {
   13213          15 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13214          15 :     return hAttr->m_poImpl->ReadAsInt64();
   13215             : }
   13216             : 
   13217             : /************************************************************************/
   13218             : /*                       GDALAttributeReadAsDouble()                    */
   13219             : /************************************************************************/
   13220             : 
   13221             : /** Return the value of an attribute as a double.
   13222             :  *
   13223             :  * This function will only return the first element if there are several.
   13224             :  *
   13225             :  * It can fail if its value can not be converted to double.
   13226             :  *
   13227             :  * This is the same as the C++ method GDALAttribute::ReadAsDouble()
   13228             :  *
   13229             :  * @return a double value.
   13230             :  */
   13231          40 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
   13232             : {
   13233          40 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13234          40 :     return hAttr->m_poImpl->ReadAsDouble();
   13235             : }
   13236             : 
   13237             : /************************************************************************/
   13238             : /*                     GDALAttributeReadAsStringArray()                 */
   13239             : /************************************************************************/
   13240             : 
   13241             : /** Return the value of an attribute as an array of strings.
   13242             :  *
   13243             :  * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
   13244             :  *
   13245             :  * The return value must be freed with CSLDestroy().
   13246             :  */
   13247          19 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
   13248             : {
   13249          19 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13250          19 :     return hAttr->m_poImpl->ReadAsStringArray().StealList();
   13251             : }
   13252             : 
   13253             : /************************************************************************/
   13254             : /*                     GDALAttributeReadAsIntArray()                    */
   13255             : /************************************************************************/
   13256             : 
   13257             : /** Return the value of an attribute as an array of integers.
   13258             :  *
   13259             :  * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
   13260             :  *
   13261             :  * @param hAttr Attribute
   13262             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13263             :  * @return array to be freed with CPLFree(), or nullptr.
   13264             :  */
   13265          15 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
   13266             : {
   13267          15 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13268          15 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13269          15 :     *pnCount = 0;
   13270          30 :     auto tmp(hAttr->m_poImpl->ReadAsIntArray());
   13271          15 :     if (tmp.empty())
   13272           0 :         return nullptr;
   13273          15 :     auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
   13274          15 :     if (!ret)
   13275           0 :         return nullptr;
   13276          15 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
   13277          15 :     *pnCount = tmp.size();
   13278          15 :     return ret;
   13279             : }
   13280             : 
   13281             : /************************************************************************/
   13282             : /*                     GDALAttributeReadAsInt64Array()                  */
   13283             : /************************************************************************/
   13284             : 
   13285             : /** Return the value of an attribute as an array of int64_t.
   13286             :  *
   13287             :  * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
   13288             :  *
   13289             :  * @param hAttr Attribute
   13290             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13291             :  * @return array to be freed with CPLFree(), or nullptr.
   13292             :  */
   13293          14 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
   13294             : {
   13295          14 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13296          14 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13297          14 :     *pnCount = 0;
   13298          28 :     auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
   13299          14 :     if (tmp.empty())
   13300           0 :         return nullptr;
   13301             :     auto ret = static_cast<int64_t *>(
   13302          14 :         VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
   13303          14 :     if (!ret)
   13304           0 :         return nullptr;
   13305          14 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
   13306          14 :     *pnCount = tmp.size();
   13307          14 :     return ret;
   13308             : }
   13309             : 
   13310             : /************************************************************************/
   13311             : /*                     GDALAttributeReadAsDoubleArray()                 */
   13312             : /************************************************************************/
   13313             : 
   13314             : /** Return the value of an attribute as an array of doubles.
   13315             :  *
   13316             :  * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
   13317             :  *
   13318             :  * @param hAttr Attribute
   13319             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13320             :  * @return array to be freed with CPLFree(), or nullptr.
   13321             :  */
   13322          29 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
   13323             : {
   13324          29 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13325          29 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13326          29 :     *pnCount = 0;
   13327          58 :     auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
   13328          29 :     if (tmp.empty())
   13329           0 :         return nullptr;
   13330             :     auto ret =
   13331          29 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
   13332          29 :     if (!ret)
   13333           0 :         return nullptr;
   13334          29 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
   13335          29 :     *pnCount = tmp.size();
   13336          29 :     return ret;
   13337             : }
   13338             : 
   13339             : /************************************************************************/
   13340             : /*                     GDALAttributeWriteRaw()                          */
   13341             : /************************************************************************/
   13342             : 
   13343             : /** Write an attribute from raw values expressed in GetDataType()
   13344             :  *
   13345             :  * The values should be provided in the type of GetDataType() and there should
   13346             :  * be exactly GetTotalElementsCount() of them.
   13347             :  * If GetDataType() is a string, each value should be a char* pointer.
   13348             :  *
   13349             :  * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
   13350             :  *
   13351             :  * @param hAttr Attribute
   13352             :  * @param pabyValue Buffer of nLen bytes.
   13353             :  * @param nLength Size of pabyValue in bytes. Should be equal to
   13354             :  *             GetTotalElementsCount() * GetDataType().GetSize()
   13355             :  * @return TRUE in case of success.
   13356             :  */
   13357           5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
   13358             :                           size_t nLength)
   13359             : {
   13360           5 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13361           5 :     return hAttr->m_poImpl->Write(pabyValue, nLength);
   13362             : }
   13363             : 
   13364             : /************************************************************************/
   13365             : /*                     GDALAttributeWriteString()                       */
   13366             : /************************************************************************/
   13367             : 
   13368             : /** Write an attribute from a string value.
   13369             :  *
   13370             :  * Type conversion will be performed if needed. If the attribute contains
   13371             :  * multiple values, only the first one will be updated.
   13372             :  *
   13373             :  * This is the same as the C++ method GDALAttribute::Write(const char*)
   13374             :  *
   13375             :  * @param hAttr Attribute
   13376             :  * @param pszVal Pointer to a string.
   13377             :  * @return TRUE in case of success.
   13378             :  */
   13379         175 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
   13380             : {
   13381         175 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13382         175 :     return hAttr->m_poImpl->Write(pszVal);
   13383             : }
   13384             : 
   13385             : /************************************************************************/
   13386             : /*                        GDALAttributeWriteInt()                       */
   13387             : /************************************************************************/
   13388             : 
   13389             : /** Write an attribute from a integer value.
   13390             :  *
   13391             :  * Type conversion will be performed if needed. If the attribute contains
   13392             :  * multiple values, only the first one will be updated.
   13393             :  *
   13394             :  * This is the same as the C++ method GDALAttribute::WriteInt()
   13395             :  *
   13396             :  * @param hAttr Attribute
   13397             :  * @param nVal Value.
   13398             :  * @return TRUE in case of success.
   13399             :  */
   13400          22 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
   13401             : {
   13402          22 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13403          22 :     return hAttr->m_poImpl->WriteInt(nVal);
   13404             : }
   13405             : 
   13406             : /************************************************************************/
   13407             : /*                        GDALAttributeWriteInt64()                     */
   13408             : /************************************************************************/
   13409             : 
   13410             : /** Write an attribute from an int64_t value.
   13411             :  *
   13412             :  * Type conversion will be performed if needed. If the attribute contains
   13413             :  * multiple values, only the first one will be updated.
   13414             :  *
   13415             :  * This is the same as the C++ method GDALAttribute::WriteLong()
   13416             :  *
   13417             :  * @param hAttr Attribute
   13418             :  * @param nVal Value.
   13419             :  * @return TRUE in case of success.
   13420             :  */
   13421          11 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
   13422             : {
   13423          11 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13424          11 :     return hAttr->m_poImpl->WriteInt64(nVal);
   13425             : }
   13426             : 
   13427             : /************************************************************************/
   13428             : /*                        GDALAttributeWriteDouble()                    */
   13429             : /************************************************************************/
   13430             : 
   13431             : /** Write an attribute from a double value.
   13432             :  *
   13433             :  * Type conversion will be performed if needed. If the attribute contains
   13434             :  * multiple values, only the first one will be updated.
   13435             :  *
   13436             :  * This is the same as the C++ method GDALAttribute::Write(double);
   13437             :  *
   13438             :  * @param hAttr Attribute
   13439             :  * @param dfVal Value.
   13440             :  *
   13441             :  * @return TRUE in case of success.
   13442             :  */
   13443          11 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
   13444             : {
   13445          11 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13446          11 :     return hAttr->m_poImpl->Write(dfVal);
   13447             : }
   13448             : 
   13449             : /************************************************************************/
   13450             : /*                       GDALAttributeWriteStringArray()                */
   13451             : /************************************************************************/
   13452             : 
   13453             : /** Write an attribute from an array of strings.
   13454             :  *
   13455             :  * Type conversion will be performed if needed.
   13456             :  *
   13457             :  * Exactly GetTotalElementsCount() strings must be provided
   13458             :  *
   13459             :  * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
   13460             :  *
   13461             :  * @param hAttr Attribute
   13462             :  * @param papszValues Array of strings.
   13463             :  * @return TRUE in case of success.
   13464             :  */
   13465           8 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
   13466             :                                   CSLConstList papszValues)
   13467             : {
   13468           8 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13469           8 :     return hAttr->m_poImpl->Write(papszValues);
   13470             : }
   13471             : 
   13472             : /************************************************************************/
   13473             : /*                       GDALAttributeWriteIntArray()                */
   13474             : /************************************************************************/
   13475             : 
   13476             : /** Write an attribute from an array of int.
   13477             :  *
   13478             :  * Type conversion will be performed if needed.
   13479             :  *
   13480             :  * Exactly GetTotalElementsCount() strings must be provided
   13481             :  *
   13482             :  * This is the same as the C++ method GDALAttribute::Write(const int *,
   13483             :  * size_t)
   13484             :  *
   13485             :  * @param hAttr Attribute
   13486             :  * @param panValues Array of int.
   13487             :  * @param nCount Should be equal to GetTotalElementsCount().
   13488             :  * @return TRUE in case of success.
   13489             :  */
   13490           9 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
   13491             :                                size_t nCount)
   13492             : {
   13493           9 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13494           9 :     return hAttr->m_poImpl->Write(panValues, nCount);
   13495             : }
   13496             : 
   13497             : /************************************************************************/
   13498             : /*                       GDALAttributeWriteInt64Array()                 */
   13499             : /************************************************************************/
   13500             : 
   13501             : /** Write an attribute from an array of int64_t.
   13502             :  *
   13503             :  * Type conversion will be performed if needed.
   13504             :  *
   13505             :  * Exactly GetTotalElementsCount() strings must be provided
   13506             :  *
   13507             :  * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
   13508             :  * size_t)
   13509             :  *
   13510             :  * @param hAttr Attribute
   13511             :  * @param panValues Array of int64_t.
   13512             :  * @param nCount Should be equal to GetTotalElementsCount().
   13513             :  * @return TRUE in case of success.
   13514             :  */
   13515          10 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
   13516             :                                  size_t nCount)
   13517             : {
   13518          10 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13519          10 :     return hAttr->m_poImpl->Write(panValues, nCount);
   13520             : }
   13521             : 
   13522             : /************************************************************************/
   13523             : /*                       GDALAttributeWriteDoubleArray()                */
   13524             : /************************************************************************/
   13525             : 
   13526             : /** Write an attribute from an array of double.
   13527             :  *
   13528             :  * Type conversion will be performed if needed.
   13529             :  *
   13530             :  * Exactly GetTotalElementsCount() strings must be provided
   13531             :  *
   13532             :  * This is the same as the C++ method GDALAttribute::Write(const double *,
   13533             :  * size_t)
   13534             :  *
   13535             :  * @param hAttr Attribute
   13536             :  * @param padfValues Array of double.
   13537             :  * @param nCount Should be equal to GetTotalElementsCount().
   13538             :  * @return TRUE in case of success.
   13539             :  */
   13540           7 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
   13541             :                                   const double *padfValues, size_t nCount)
   13542             : {
   13543           7 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13544           7 :     return hAttr->m_poImpl->Write(padfValues, nCount);
   13545             : }
   13546             : 
   13547             : /************************************************************************/
   13548             : /*                      GDALAttributeRename()                           */
   13549             : /************************************************************************/
   13550             : 
   13551             : /** Rename the attribute.
   13552             :  *
   13553             :  * This is not implemented by all drivers.
   13554             :  *
   13555             :  * Drivers known to implement it: MEM, netCDF.
   13556             :  *
   13557             :  * This is the same as the C++ method GDALAbstractMDArray::Rename()
   13558             :  *
   13559             :  * @return true in case of success
   13560             :  * @since GDAL 3.8
   13561             :  */
   13562          27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
   13563             : {
   13564          27 :     VALIDATE_POINTER1(hAttr, __func__, false);
   13565          27 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   13566          27 :     return hAttr->m_poImpl->Rename(pszNewName);
   13567             : }
   13568             : 
   13569             : /************************************************************************/
   13570             : /*                        GDALDimensionRelease()                        */
   13571             : /************************************************************************/
   13572             : 
   13573             : /** Release the GDAL in-memory object associated with a GDALDimension.
   13574             :  *
   13575             :  * Note: when applied on a object coming from a driver, this does not
   13576             :  * destroy the object in the file, database, etc...
   13577             :  */
   13578        4881 : void GDALDimensionRelease(GDALDimensionH hDim)
   13579             : {
   13580        4881 :     delete hDim;
   13581        4881 : }
   13582             : 
   13583             : /************************************************************************/
   13584             : /*                        GDALDimensionGetName()                        */
   13585             : /************************************************************************/
   13586             : 
   13587             : /** Return dimension name.
   13588             :  *
   13589             :  * This is the same as the C++ method GDALDimension::GetName()
   13590             :  */
   13591         284 : const char *GDALDimensionGetName(GDALDimensionH hDim)
   13592             : {
   13593         284 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13594         284 :     return hDim->m_poImpl->GetName().c_str();
   13595             : }
   13596             : 
   13597             : /************************************************************************/
   13598             : /*                      GDALDimensionGetFullName()                      */
   13599             : /************************************************************************/
   13600             : 
   13601             : /** Return dimension full name.
   13602             :  *
   13603             :  * This is the same as the C++ method GDALDimension::GetFullName()
   13604             :  */
   13605          80 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
   13606             : {
   13607          80 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13608          80 :     return hDim->m_poImpl->GetFullName().c_str();
   13609             : }
   13610             : 
   13611             : /************************************************************************/
   13612             : /*                        GDALDimensionGetType()                        */
   13613             : /************************************************************************/
   13614             : 
   13615             : /** Return dimension type.
   13616             :  *
   13617             :  * This is the same as the C++ method GDALDimension::GetType()
   13618             :  */
   13619          62 : const char *GDALDimensionGetType(GDALDimensionH hDim)
   13620             : {
   13621          62 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13622          62 :     return hDim->m_poImpl->GetType().c_str();
   13623             : }
   13624             : 
   13625             : /************************************************************************/
   13626             : /*                     GDALDimensionGetDirection()                      */
   13627             : /************************************************************************/
   13628             : 
   13629             : /** Return dimension direction.
   13630             :  *
   13631             :  * This is the same as the C++ method GDALDimension::GetDirection()
   13632             :  */
   13633          32 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
   13634             : {
   13635          32 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13636          32 :     return hDim->m_poImpl->GetDirection().c_str();
   13637             : }
   13638             : 
   13639             : /************************************************************************/
   13640             : /*                        GDALDimensionGetSize()                        */
   13641             : /************************************************************************/
   13642             : 
   13643             : /** Return the size, that is the number of values along the dimension.
   13644             :  *
   13645             :  * This is the same as the C++ method GDALDimension::GetSize()
   13646             :  */
   13647        3654 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
   13648             : {
   13649        3654 :     VALIDATE_POINTER1(hDim, __func__, 0);
   13650        3654 :     return hDim->m_poImpl->GetSize();
   13651             : }
   13652             : 
   13653             : /************************************************************************/
   13654             : /*                     GDALDimensionGetIndexingVariable()               */
   13655             : /************************************************************************/
   13656             : 
   13657             : /** Return the variable that is used to index the dimension (if there is one).
   13658             :  *
   13659             :  * This is the array, typically one-dimensional, describing the values taken
   13660             :  * by the dimension.
   13661             :  *
   13662             :  * The returned value should be freed with GDALMDArrayRelease().
   13663             :  *
   13664             :  * This is the same as the C++ method GDALDimension::GetIndexingVariable()
   13665             :  */
   13666         118 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
   13667             : {
   13668         118 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13669         236 :     auto var(hDim->m_poImpl->GetIndexingVariable());
   13670         118 :     if (!var)
   13671          10 :         return nullptr;
   13672         108 :     return new GDALMDArrayHS(var);
   13673             : }
   13674             : 
   13675             : /************************************************************************/
   13676             : /*                      GDALDimensionSetIndexingVariable()              */
   13677             : /************************************************************************/
   13678             : 
   13679             : /** Set the variable that is used to index the dimension.
   13680             :  *
   13681             :  * This is the array, typically one-dimensional, describing the values taken
   13682             :  * by the dimension.
   13683             :  *
   13684             :  * This is the same as the C++ method GDALDimension::SetIndexingVariable()
   13685             :  *
   13686             :  * @return TRUE in case of success.
   13687             :  */
   13688          23 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
   13689             : {
   13690          23 :     VALIDATE_POINTER1(hDim, __func__, FALSE);
   13691          69 :     return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
   13692          46 :                                                       : nullptr);
   13693             : }
   13694             : 
   13695             : /************************************************************************/
   13696             : /*                      GDALDimensionRename()                           */
   13697             : /************************************************************************/
   13698             : 
   13699             : /** Rename the dimension.
   13700             :  *
   13701             :  * This is not implemented by all drivers.
   13702             :  *
   13703             :  * Drivers known to implement it: MEM, netCDF.
   13704             :  *
   13705             :  * This is the same as the C++ method GDALDimension::Rename()
   13706             :  *
   13707             :  * @return true in case of success
   13708             :  * @since GDAL 3.8
   13709             :  */
   13710          31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
   13711             : {
   13712          31 :     VALIDATE_POINTER1(hDim, __func__, false);
   13713          31 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   13714          31 :     return hDim->m_poImpl->Rename(pszNewName);
   13715             : }
   13716             : 
   13717             : /************************************************************************/
   13718             : /*                       GDALDatasetGetRootGroup()                      */
   13719             : /************************************************************************/
   13720             : 
   13721             : /** Return the root GDALGroup of this dataset.
   13722             :  *
   13723             :  * Only valid for multidimensional datasets.
   13724             :  *
   13725             :  * The returned value must be freed with GDALGroupRelease().
   13726             :  *
   13727             :  * This is the same as the C++ method GDALDataset::GetRootGroup().
   13728             :  *
   13729             :  * @since GDAL 3.1
   13730             :  */
   13731        1145 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
   13732             : {
   13733        1145 :     VALIDATE_POINTER1(hDS, __func__, nullptr);
   13734        1145 :     auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
   13735        1145 :     return poGroup ? new GDALGroupHS(poGroup) : nullptr;
   13736             : }
   13737             : 
   13738             : /************************************************************************/
   13739             : /*                      GDALRasterBandAsMDArray()                        */
   13740             : /************************************************************************/
   13741             : 
   13742             : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
   13743             :  *
   13744             :  * The band must be linked to a GDALDataset. If this dataset is not already
   13745             :  * marked as shared, it will be, so that the returned array holds a reference
   13746             :  * to it.
   13747             :  *
   13748             :  * If the dataset has a geotransform attached, the X and Y dimensions of the
   13749             :  * returned array will have an associated indexing variable.
   13750             :  *
   13751             :  * The returned pointer must be released with GDALMDArrayRelease().
   13752             :  *
   13753             :  * This is the same as the C++ method GDALRasterBand::AsMDArray().
   13754             :  *
   13755             :  * @return a new array, or NULL.
   13756             :  *
   13757             :  * @since GDAL 3.1
   13758             :  */
   13759          21 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
   13760             : {
   13761          21 :     VALIDATE_POINTER1(hBand, __func__, nullptr);
   13762          42 :     auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
   13763          21 :     if (!poArray)
   13764           0 :         return nullptr;
   13765          21 :     return new GDALMDArrayHS(poArray);
   13766             : }
   13767             : 
   13768             : /************************************************************************/
   13769             : /*                       GDALMDArrayAsClassicDataset()                  */
   13770             : /************************************************************************/
   13771             : 
   13772             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   13773             :  *
   13774             :  * Only 2D or more arrays are supported.
   13775             :  *
   13776             :  * In the case of > 2D arrays, additional dimensions will be represented as
   13777             :  * raster bands.
   13778             :  *
   13779             :  * The "reverse" method is GDALRasterBand::AsMDArray().
   13780             :  *
   13781             :  * This is the same as the C++ method GDALMDArray::AsClassicDataset().
   13782             :  *
   13783             :  * @param hArray Array.
   13784             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   13785             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   13786             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   13787             :  */
   13788           0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
   13789             :                                          size_t iYDim)
   13790             : {
   13791           0 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13792           0 :     return GDALDataset::ToHandle(
   13793           0 :         hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
   13794             : }
   13795             : 
   13796             : /************************************************************************/
   13797             : /*                     GDALMDArrayAsClassicDatasetEx()                  */
   13798             : /************************************************************************/
   13799             : 
   13800             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   13801             :  *
   13802             :  * Only 2D or more arrays are supported.
   13803             :  *
   13804             :  * In the case of > 2D arrays, additional dimensions will be represented as
   13805             :  * raster bands.
   13806             :  *
   13807             :  * The "reverse" method is GDALRasterBand::AsMDArray().
   13808             :  *
   13809             :  * This is the same as the C++ method GDALMDArray::AsClassicDataset().
   13810             :  * @param hArray Array.
   13811             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   13812             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   13813             :  *              Ignored if the dimension count is 1.
   13814             :  * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
   13815             :  *                   BAND_IMAGERY_METADATA option.
   13816             :  * @param papszOptions Cf GDALMDArray::AsClassicDataset()
   13817             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   13818             :  * @since GDAL 3.8
   13819             :  */
   13820          71 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
   13821             :                                            size_t iYDim, GDALGroupH hRootGroup,
   13822             :                                            CSLConstList papszOptions)
   13823             : {
   13824          71 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13825         142 :     return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
   13826         142 :         iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
   13827         142 :         papszOptions));
   13828             : }
   13829             : 
   13830             : //! @cond Doxygen_Suppress
   13831             : 
   13832         180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
   13833             :                                          const std::string &osName,
   13834             :                                          const std::string &osValue,
   13835         180 :                                          GDALExtendedDataTypeSubType eSubType)
   13836             :     : GDALAbstractMDArray(osParentName, osName),
   13837             :       GDALAttribute(osParentName, osName),
   13838         180 :       m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
   13839             : {
   13840         180 : }
   13841             : 
   13842             : const std::vector<std::shared_ptr<GDALDimension>> &
   13843          30 : GDALAttributeString::GetDimensions() const
   13844             : {
   13845          30 :     return m_dims;
   13846             : }
   13847             : 
   13848          21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
   13849             : {
   13850          21 :     return m_dt;
   13851             : }
   13852             : 
   13853          10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
   13854             :                                 const GPtrDiff_t *,
   13855             :                                 const GDALExtendedDataType &bufferDataType,
   13856             :                                 void *pDstBuffer) const
   13857             : {
   13858          10 :     if (bufferDataType.GetClass() != GEDTC_STRING)
   13859           0 :         return false;
   13860          10 :     char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
   13861          10 :     if (!pszStr)
   13862           0 :         return false;
   13863          10 :     memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
   13864          10 :     *static_cast<char **>(pDstBuffer) = pszStr;
   13865          10 :     return true;
   13866             : }
   13867             : 
   13868          66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   13869             :                                            const std::string &osName,
   13870          66 :                                            double dfValue)
   13871             :     : GDALAbstractMDArray(osParentName, osName),
   13872             :       GDALAttribute(osParentName, osName),
   13873          66 :       m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
   13874             : {
   13875          66 : }
   13876             : 
   13877          27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   13878             :                                            const std::string &osName,
   13879          27 :                                            int nValue)
   13880             :     : GDALAbstractMDArray(osParentName, osName),
   13881             :       GDALAttribute(osParentName, osName),
   13882          27 :       m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
   13883             : {
   13884          27 : }
   13885             : 
   13886           7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   13887             :                                            const std::string &osName,
   13888           7 :                                            const std::vector<GUInt32> &anValues)
   13889             :     : GDALAbstractMDArray(osParentName, osName),
   13890             :       GDALAttribute(osParentName, osName),
   13891           7 :       m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
   13892             : {
   13893           7 :     m_dims.push_back(std::make_shared<GDALDimension>(
   13894          14 :         std::string(), "dim0", std::string(), std::string(),
   13895           7 :         m_anValuesUInt32.size()));
   13896           7 : }
   13897             : 
   13898             : const std::vector<std::shared_ptr<GDALDimension>> &
   13899          14 : GDALAttributeNumeric::GetDimensions() const
   13900             : {
   13901          14 :     return m_dims;
   13902             : }
   13903             : 
   13904           8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
   13905             : {
   13906           8 :     return m_dt;
   13907             : }
   13908             : 
   13909           4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
   13910             :                                  const size_t *count, const GInt64 *arrayStep,
   13911             :                                  const GPtrDiff_t *bufferStride,
   13912             :                                  const GDALExtendedDataType &bufferDataType,
   13913             :                                  void *pDstBuffer) const
   13914             : {
   13915           4 :     if (m_dims.empty())
   13916             :     {
   13917           3 :         if (m_dt.GetNumericDataType() == GDT_Float64)
   13918           0 :             GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
   13919             :                                             bufferDataType);
   13920             :         else
   13921             :         {
   13922           3 :             CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
   13923           3 :             GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
   13924             :                                             bufferDataType);
   13925             :         }
   13926             :     }
   13927             :     else
   13928             :     {
   13929           1 :         CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
   13930           1 :         GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
   13931          30 :         for (size_t i = 0; i < count[0]; ++i)
   13932             :         {
   13933          29 :             GDALExtendedDataType::CopyValue(
   13934          29 :                 &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
   13935          29 :                                                       i * arrayStep[0])],
   13936          29 :                 m_dt, pabyDstBuffer, bufferDataType);
   13937          29 :             pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
   13938             :         }
   13939             :     }
   13940           4 :     return true;
   13941             : }
   13942             : 
   13943         192 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
   13944             :     const std::string &osParentName, const std::string &osName,
   13945             :     const std::shared_ptr<GDALDimension> &poDim, double dfStart,
   13946         192 :     double dfIncrement, double dfOffsetInIncrement)
   13947             :     : GDALAbstractMDArray(osParentName, osName),
   13948             :       GDALMDArray(osParentName, osName), m_dfStart(dfStart),
   13949             :       m_dfIncrement(dfIncrement),
   13950         384 :       m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
   13951             : {
   13952         192 : }
   13953             : 
   13954         192 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
   13955             :     const std::string &osParentName, const std::string &osName,
   13956             :     const std::shared_ptr<GDALDimension> &poDim, double dfStart,
   13957             :     double dfIncrement, double dfOffsetInIncrement)
   13958             : {
   13959             :     auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
   13960         192 :         osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
   13961         192 :     poArray->SetSelf(poArray);
   13962         192 :     return poArray;
   13963             : }
   13964             : 
   13965             : const std::vector<std::shared_ptr<GDALDimension>> &
   13966         786 : GDALMDArrayRegularlySpaced::GetDimensions() const
   13967             : {
   13968         786 :     return m_dims;
   13969             : }
   13970             : 
   13971         316 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
   13972             : {
   13973         316 :     return m_dt;
   13974             : }
   13975             : 
   13976             : std::vector<std::shared_ptr<GDALAttribute>>
   13977           4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
   13978             : {
   13979           4 :     return m_attributes;
   13980             : }
   13981             : 
   13982           0 : void GDALMDArrayRegularlySpaced::AddAttribute(
   13983             :     const std::shared_ptr<GDALAttribute> &poAttr)
   13984             : {
   13985           0 :     m_attributes.emplace_back(poAttr);
   13986           0 : }
   13987             : 
   13988         188 : bool GDALMDArrayRegularlySpaced::IRead(
   13989             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
   13990             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
   13991             :     void *pDstBuffer) const
   13992             : {
   13993         188 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
   13994       14719 :     for (size_t i = 0; i < count[0]; i++)
   13995             :     {
   13996       14531 :         const double dfVal = m_dfStart + (arrayStartIdx[0] + i * arrayStep[0] +
   13997       14531 :                                           m_dfOffsetInIncrement) *
   13998       14531 :                                              m_dfIncrement;
   13999       14531 :         GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
   14000             :                                         bufferDataType);
   14001       14531 :         pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
   14002             :     }
   14003         188 :     return true;
   14004             : }
   14005             : 
   14006        2950 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
   14007             :     const std::string &osParentName, const std::string &osName,
   14008        2950 :     const std::string &osType, const std::string &osDirection, GUInt64 nSize)
   14009        2950 :     : GDALDimension(osParentName, osName, osType, osDirection, nSize)
   14010             : {
   14011        2950 : }
   14012             : 
   14013             : std::shared_ptr<GDALMDArray>
   14014         706 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
   14015             : {
   14016         706 :     return m_poIndexingVariable.lock();
   14017             : }
   14018             : 
   14019             : // cppcheck-suppress passedByValue
   14020         488 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
   14021             :     std::shared_ptr<GDALMDArray> poIndexingVariable)
   14022             : {
   14023         488 :     m_poIndexingVariable = poIndexingVariable;
   14024         488 :     return true;
   14025             : }
   14026             : 
   14027          33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
   14028             : {
   14029          33 :     m_nSize = nNewSize;
   14030          33 : }
   14031             : 
   14032             : /************************************************************************/
   14033             : /*                       GDALPamMultiDim::Private                       */
   14034             : /************************************************************************/
   14035             : 
   14036             : struct GDALPamMultiDim::Private
   14037             : {
   14038             :     std::string m_osFilename{};
   14039             :     std::string m_osPamFilename{};
   14040             : 
   14041             :     struct Statistics
   14042             :     {
   14043             :         bool bHasStats = false;
   14044             :         bool bApproxStats = false;
   14045             :         double dfMin = 0;
   14046             :         double dfMax = 0;
   14047             :         double dfMean = 0;
   14048             :         double dfStdDev = 0;
   14049             :         GUInt64 nValidCount = 0;
   14050             :     };
   14051             : 
   14052             :     struct ArrayInfo
   14053             :     {
   14054             :         std::shared_ptr<OGRSpatialReference> poSRS{};
   14055             :         // cppcheck-suppress unusedStructMember
   14056             :         Statistics stats{};
   14057             :     };
   14058             : 
   14059             :     typedef std::pair<std::string, std::string> NameContext;
   14060             :     std::map<NameContext, ArrayInfo> m_oMapArray{};
   14061             :     std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
   14062             :     bool m_bDirty = false;
   14063             :     bool m_bLoaded = false;
   14064             : };
   14065             : 
   14066             : /************************************************************************/
   14067             : /*                          GDALPamMultiDim                             */
   14068             : /************************************************************************/
   14069             : 
   14070        1388 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
   14071        1388 :     : d(new Private())
   14072             : {
   14073        1388 :     d->m_osFilename = osFilename;
   14074        1388 : }
   14075             : 
   14076             : /************************************************************************/
   14077             : /*                   GDALPamMultiDim::~GDALPamMultiDim()                */
   14078             : /************************************************************************/
   14079             : 
   14080        1388 : GDALPamMultiDim::~GDALPamMultiDim()
   14081             : {
   14082        1388 :     if (d->m_bDirty)
   14083          29 :         Save();
   14084        1388 : }
   14085             : 
   14086             : /************************************************************************/
   14087             : /*                          GDALPamMultiDim::Load()                     */
   14088             : /************************************************************************/
   14089             : 
   14090         101 : void GDALPamMultiDim::Load()
   14091             : {
   14092         101 :     if (d->m_bLoaded)
   14093          90 :         return;
   14094          44 :     d->m_bLoaded = true;
   14095             : 
   14096          44 :     const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
   14097          44 :     d->m_osPamFilename =
   14098          88 :         pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
   14099          44 :     CPLXMLTreeCloser oTree(nullptr);
   14100             :     {
   14101          88 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
   14102          44 :         oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
   14103             :     }
   14104          44 :     if (!oTree)
   14105             :     {
   14106          33 :         return;
   14107             :     }
   14108          11 :     const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
   14109          11 :     if (!poPAMMultiDim)
   14110           0 :         return;
   14111          35 :     for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
   14112          24 :          psIter = psIter->psNext)
   14113             :     {
   14114          24 :         if (psIter->eType == CXT_Element &&
   14115          24 :             strcmp(psIter->pszValue, "Array") == 0)
   14116             :         {
   14117          13 :             const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
   14118          13 :             if (!pszName)
   14119           0 :                 continue;
   14120          13 :             const char *pszContext = CPLGetXMLValue(psIter, "context", "");
   14121             :             const auto oKey =
   14122          26 :                 std::pair<std::string, std::string>(pszName, pszContext);
   14123             : 
   14124             :             /* --------------------------------------------------------------------
   14125             :              */
   14126             :             /*      Check for an SRS node. */
   14127             :             /* --------------------------------------------------------------------
   14128             :              */
   14129          13 :             const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
   14130          13 :             if (psSRSNode)
   14131             :             {
   14132             :                 std::shared_ptr<OGRSpatialReference> poSRS =
   14133           6 :                     std::make_shared<OGRSpatialReference>();
   14134           3 :                 poSRS->SetFromUserInput(
   14135             :                     CPLGetXMLValue(psSRSNode, nullptr, ""),
   14136             :                     OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
   14137           3 :                 const char *pszMapping = CPLGetXMLValue(
   14138             :                     psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
   14139           3 :                 if (pszMapping)
   14140             :                 {
   14141             :                     char **papszTokens =
   14142           3 :                         CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
   14143           6 :                     std::vector<int> anMapping;
   14144           9 :                     for (int i = 0; papszTokens && papszTokens[i]; i++)
   14145             :                     {
   14146           6 :                         anMapping.push_back(atoi(papszTokens[i]));
   14147             :                     }
   14148           3 :                     CSLDestroy(papszTokens);
   14149           3 :                     poSRS->SetDataAxisToSRSAxisMapping(anMapping);
   14150             :                 }
   14151             :                 else
   14152             :                 {
   14153           0 :                     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
   14154             :                 }
   14155             : 
   14156             :                 const char *pszCoordinateEpoch =
   14157           3 :                     CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
   14158           3 :                 if (pszCoordinateEpoch)
   14159           3 :                     poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
   14160             : 
   14161           3 :                 d->m_oMapArray[oKey].poSRS = std::move(poSRS);
   14162             :             }
   14163             : 
   14164             :             const CPLXMLNode *psStatistics =
   14165          13 :                 CPLGetXMLNode(psIter, "Statistics");
   14166          13 :             if (psStatistics)
   14167             :             {
   14168           7 :                 Private::Statistics sStats;
   14169           7 :                 sStats.bHasStats = true;
   14170           7 :                 sStats.bApproxStats = CPLTestBool(
   14171             :                     CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
   14172           7 :                 sStats.dfMin =
   14173           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
   14174           7 :                 sStats.dfMax =
   14175           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
   14176           7 :                 sStats.dfMean =
   14177           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
   14178           7 :                 sStats.dfStdDev =
   14179           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
   14180           7 :                 sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
   14181             :                     CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
   14182           7 :                 d->m_oMapArray[oKey].stats = sStats;
   14183          13 :             }
   14184             :         }
   14185             :         else
   14186             :         {
   14187          11 :             CPLXMLNode *psNextBackup = psIter->psNext;
   14188          11 :             psIter->psNext = nullptr;
   14189          11 :             d->m_apoOtherNodes.emplace_back(
   14190          11 :                 CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
   14191          11 :             psIter->psNext = psNextBackup;
   14192             :         }
   14193             :     }
   14194             : }
   14195             : 
   14196             : /************************************************************************/
   14197             : /*                          GDALPamMultiDim::Save()                     */
   14198             : /************************************************************************/
   14199             : 
   14200          29 : void GDALPamMultiDim::Save()
   14201             : {
   14202             :     CPLXMLTreeCloser oTree(
   14203          58 :         CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
   14204          33 :     for (const auto &poOtherNode : d->m_apoOtherNodes)
   14205             :     {
   14206           4 :         CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
   14207             :     }
   14208         108 :     for (const auto &kv : d->m_oMapArray)
   14209             :     {
   14210             :         CPLXMLNode *psArrayNode =
   14211          79 :             CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
   14212          79 :         CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
   14213          79 :         if (!kv.first.second.empty())
   14214             :         {
   14215           1 :             CPLAddXMLAttributeAndValue(psArrayNode, "context",
   14216             :                                        kv.first.second.c_str());
   14217             :         }
   14218          79 :         if (kv.second.poSRS)
   14219             :         {
   14220          71 :             char *pszWKT = nullptr;
   14221             :             {
   14222         142 :                 CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
   14223          71 :                 const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
   14224          71 :                 kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
   14225             :             }
   14226             :             CPLXMLNode *psSRSNode =
   14227          71 :                 CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
   14228          71 :             CPLFree(pszWKT);
   14229             :             const auto &mapping =
   14230          71 :                 kv.second.poSRS->GetDataAxisToSRSAxisMapping();
   14231         142 :             CPLString osMapping;
   14232         213 :             for (size_t i = 0; i < mapping.size(); ++i)
   14233             :             {
   14234         142 :                 if (!osMapping.empty())
   14235          71 :                     osMapping += ",";
   14236         142 :                 osMapping += CPLSPrintf("%d", mapping[i]);
   14237             :             }
   14238          71 :             CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
   14239             :                                        osMapping.c_str());
   14240             : 
   14241             :             const double dfCoordinateEpoch =
   14242          71 :                 kv.second.poSRS->GetCoordinateEpoch();
   14243          71 :             if (dfCoordinateEpoch > 0)
   14244             :             {
   14245             :                 std::string osCoordinateEpoch =
   14246           2 :                     CPLSPrintf("%f", dfCoordinateEpoch);
   14247           1 :                 if (osCoordinateEpoch.find('.') != std::string::npos)
   14248             :                 {
   14249           6 :                     while (osCoordinateEpoch.back() == '0')
   14250           5 :                         osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
   14251             :                 }
   14252           1 :                 CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
   14253             :                                            osCoordinateEpoch.c_str());
   14254             :             }
   14255             :         }
   14256             : 
   14257          79 :         if (kv.second.stats.bHasStats)
   14258             :         {
   14259             :             CPLXMLNode *psMDArray =
   14260           5 :                 CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
   14261           5 :             CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
   14262           5 :                                         kv.second.stats.bApproxStats ? "1"
   14263             :                                                                      : "0");
   14264           5 :             CPLCreateXMLElementAndValue(
   14265             :                 psMDArray, "Minimum",
   14266           5 :                 CPLSPrintf("%.17g", kv.second.stats.dfMin));
   14267           5 :             CPLCreateXMLElementAndValue(
   14268             :                 psMDArray, "Maximum",
   14269           5 :                 CPLSPrintf("%.17g", kv.second.stats.dfMax));
   14270           5 :             CPLCreateXMLElementAndValue(
   14271           5 :                 psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
   14272           5 :             CPLCreateXMLElementAndValue(
   14273             :                 psMDArray, "StdDev",
   14274           5 :                 CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
   14275           5 :             CPLCreateXMLElementAndValue(
   14276             :                 psMDArray, "ValidSampleCount",
   14277           5 :                 CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
   14278             :         }
   14279             :     }
   14280             : 
   14281             :     int bSaved;
   14282          58 :     CPLErrorAccumulator oErrorAccumulator;
   14283             :     {
   14284          29 :         auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
   14285          29 :         CPL_IGNORE_RET_VAL(oAccumulator);
   14286             :         bSaved =
   14287          29 :             CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
   14288             :     }
   14289             : 
   14290          29 :     const char *pszNewPam = nullptr;
   14291          29 :     if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
   14292           0 :         ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
   14293             :     {
   14294           0 :         CPLErrorReset();
   14295           0 :         CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
   14296             :     }
   14297             :     else
   14298             :     {
   14299          29 :         oErrorAccumulator.ReplayErrors();
   14300             :     }
   14301          29 : }
   14302             : 
   14303             : /************************************************************************/
   14304             : /*                    GDALPamMultiDim::GetSpatialRef()                  */
   14305             : /************************************************************************/
   14306             : 
   14307             : std::shared_ptr<OGRSpatialReference>
   14308          10 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
   14309             :                                const std::string &osContext)
   14310             : {
   14311          10 :     Load();
   14312             :     auto oIter =
   14313          10 :         d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
   14314          10 :     if (oIter != d->m_oMapArray.end())
   14315           2 :         return oIter->second.poSRS;
   14316           8 :     return nullptr;
   14317             : }
   14318             : 
   14319             : /************************************************************************/
   14320             : /*                    GDALPamMultiDim::SetSpatialRef()                  */
   14321             : /************************************************************************/
   14322             : 
   14323          72 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
   14324             :                                     const std::string &osContext,
   14325             :                                     const OGRSpatialReference *poSRS)
   14326             : {
   14327          72 :     Load();
   14328          72 :     d->m_bDirty = true;
   14329          72 :     if (poSRS && !poSRS->IsEmpty())
   14330          71 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
   14331             :             poSRS->Clone());
   14332             :     else
   14333           2 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
   14334           1 :             .poSRS.reset();
   14335          72 : }
   14336             : 
   14337             : /************************************************************************/
   14338             : /*                           GetStatistics()                            */
   14339             : /************************************************************************/
   14340             : 
   14341          13 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
   14342             :                                       const std::string &osContext,
   14343             :                                       bool bApproxOK, double *pdfMin,
   14344             :                                       double *pdfMax, double *pdfMean,
   14345             :                                       double *pdfStdDev, GUInt64 *pnValidCount)
   14346             : {
   14347          13 :     Load();
   14348             :     auto oIter =
   14349          13 :         d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
   14350          13 :     if (oIter == d->m_oMapArray.end())
   14351           6 :         return CE_Failure;
   14352           7 :     const auto &stats = oIter->second.stats;
   14353           7 :     if (!stats.bHasStats)
   14354           1 :         return CE_Failure;
   14355           6 :     if (!bApproxOK && stats.bApproxStats)
   14356           0 :         return CE_Failure;
   14357           6 :     if (pdfMin)
   14358           6 :         *pdfMin = stats.dfMin;
   14359           6 :     if (pdfMax)
   14360           6 :         *pdfMax = stats.dfMax;
   14361           6 :     if (pdfMean)
   14362           6 :         *pdfMean = stats.dfMean;
   14363           6 :     if (pdfStdDev)
   14364           6 :         *pdfStdDev = stats.dfStdDev;
   14365           6 :     if (pnValidCount)
   14366           6 :         *pnValidCount = stats.nValidCount;
   14367           6 :     return CE_None;
   14368             : }
   14369             : 
   14370             : /************************************************************************/
   14371             : /*                           SetStatistics()                            */
   14372             : /************************************************************************/
   14373             : 
   14374           5 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
   14375             :                                     const std::string &osContext,
   14376             :                                     bool bApproxStats, double dfMin,
   14377             :                                     double dfMax, double dfMean,
   14378             :                                     double dfStdDev, GUInt64 nValidCount)
   14379             : {
   14380           5 :     Load();
   14381           5 :     d->m_bDirty = true;
   14382             :     auto &stats =
   14383           5 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
   14384           5 :     stats.bHasStats = true;
   14385           5 :     stats.bApproxStats = bApproxStats;
   14386           5 :     stats.dfMin = dfMin;
   14387           5 :     stats.dfMax = dfMax;
   14388           5 :     stats.dfMean = dfMean;
   14389           5 :     stats.dfStdDev = dfStdDev;
   14390           5 :     stats.nValidCount = nValidCount;
   14391           5 : }
   14392             : 
   14393             : /************************************************************************/
   14394             : /*                           ClearStatistics()                          */
   14395             : /************************************************************************/
   14396             : 
   14397           0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
   14398             :                                       const std::string &osContext)
   14399             : {
   14400           0 :     Load();
   14401           0 :     d->m_bDirty = true;
   14402           0 :     d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
   14403             :         false;
   14404           0 : }
   14405             : 
   14406             : /************************************************************************/
   14407             : /*                           ClearStatistics()                          */
   14408             : /************************************************************************/
   14409             : 
   14410           1 : void GDALPamMultiDim::ClearStatistics()
   14411             : {
   14412           1 :     Load();
   14413           1 :     d->m_bDirty = true;
   14414           3 :     for (auto &kv : d->m_oMapArray)
   14415           2 :         kv.second.stats.bHasStats = false;
   14416           1 : }
   14417             : 
   14418             : /************************************************************************/
   14419             : /*                             GetPAM()                                 */
   14420             : /************************************************************************/
   14421             : 
   14422             : /*static*/ std::shared_ptr<GDALPamMultiDim>
   14423         784 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
   14424             : {
   14425         784 :     auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
   14426         784 :     if (poPamArray)
   14427         563 :         return poPamArray->GetPAM();
   14428         221 :     return nullptr;
   14429             : }
   14430             : 
   14431             : /************************************************************************/
   14432             : /*                           GDALPamMDArray                             */
   14433             : /************************************************************************/
   14434             : 
   14435        3663 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
   14436             :                                const std::string &osName,
   14437             :                                const std::shared_ptr<GDALPamMultiDim> &poPam,
   14438           0 :                                const std::string &osContext)
   14439             :     :
   14440             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
   14441             :       GDALAbstractMDArray(osParentName, osName),
   14442             : #endif
   14443        3663 :       GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
   14444             : {
   14445        3663 : }
   14446             : 
   14447             : /************************************************************************/
   14448             : /*                    GDALPamMDArray::SetSpatialRef()                   */
   14449             : /************************************************************************/
   14450             : 
   14451          72 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
   14452             : {
   14453          72 :     if (!m_poPam)
   14454           0 :         return false;
   14455          72 :     m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
   14456          72 :     return true;
   14457             : }
   14458             : 
   14459             : /************************************************************************/
   14460             : /*                    GDALPamMDArray::GetSpatialRef()                   */
   14461             : /************************************************************************/
   14462             : 
   14463          10 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
   14464             : {
   14465          10 :     if (!m_poPam)
   14466           0 :         return nullptr;
   14467          10 :     return m_poPam->GetSpatialRef(GetFullName(), GetContext());
   14468             : }
   14469             : 
   14470             : /************************************************************************/
   14471             : /*                           GetStatistics()                            */
   14472             : /************************************************************************/
   14473             : 
   14474          13 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
   14475             :                                      double *pdfMin, double *pdfMax,
   14476             :                                      double *pdfMean, double *pdfStdDev,
   14477             :                                      GUInt64 *pnValidCount,
   14478             :                                      GDALProgressFunc pfnProgress,
   14479             :                                      void *pProgressData)
   14480             : {
   14481          13 :     if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
   14482             :                                           bApproxOK, pdfMin, pdfMax, pdfMean,
   14483          13 :                                           pdfStdDev, pnValidCount) == CE_None)
   14484             :     {
   14485           6 :         return CE_None;
   14486             :     }
   14487           7 :     if (!bForce)
   14488           4 :         return CE_Warning;
   14489             : 
   14490           3 :     return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
   14491             :                                       pdfMean, pdfStdDev, pnValidCount,
   14492           3 :                                       pfnProgress, pProgressData);
   14493             : }
   14494             : 
   14495             : /************************************************************************/
   14496             : /*                           SetStatistics()                            */
   14497             : /************************************************************************/
   14498             : 
   14499           5 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
   14500             :                                    double dfMax, double dfMean, double dfStdDev,
   14501             :                                    GUInt64 nValidCount,
   14502             :                                    CSLConstList /* papszOptions */)
   14503             : {
   14504           5 :     if (!m_poPam)
   14505           0 :         return false;
   14506           5 :     m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
   14507             :                            dfMax, dfMean, dfStdDev, nValidCount);
   14508           5 :     return true;
   14509             : }
   14510             : 
   14511             : /************************************************************************/
   14512             : /*                           ClearStatistics()                          */
   14513             : /************************************************************************/
   14514             : 
   14515           0 : void GDALPamMDArray::ClearStatistics()
   14516             : {
   14517           0 :     if (!m_poPam)
   14518           0 :         return;
   14519           0 :     m_poPam->ClearStatistics(GetFullName(), GetContext());
   14520             : }
   14521             : 
   14522             : //! @endcond

Generated by: LCOV version 1.14