LCOV - code coverage report
Current view: top level - gcore - gdalmultidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 4894 5408 90.5 %
Date: 2025-10-21 22:35:35 Functions: 472 546 86.4 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Name:     gdalmultidim.cpp
       4             :  * Project:  GDAL Core
       5             :  * Purpose:  GDAL Core C++/Private implementation for multidimensional support
       6             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include <assert.h>
      15             : #include <algorithm>
      16             : #include <limits>
      17             : #include <list>
      18             : #include <queue>
      19             : #include <set>
      20             : #include <utility>
      21             : #include <time.h>
      22             : 
      23             : #include <cmath>
      24             : #include <ctype.h>  // isalnum
      25             : 
      26             : #include "cpl_error_internal.h"
      27             : #include "cpl_float.h"
      28             : #include "gdal_priv.h"
      29             : #include "gdal_pam.h"
      30             : #include "gdal_pam_multidim.h"
      31             : #include "gdal_rat.h"
      32             : #include "gdal_utils.h"
      33             : #include "cpl_safemaths.hpp"
      34             : #include "memmultidim.h"
      35             : #include "ogrsf_frmts.h"
      36             : #include "gdalmultidim_priv.h"
      37             : 
      38             : #if defined(__clang__) || defined(_MSC_VER)
      39             : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
      40             : #endif
      41             : 
      42             : /************************************************************************/
      43             : /*                       GDALMDArrayUnscaled                            */
      44             : /************************************************************************/
      45             : 
      46             : class GDALMDArrayUnscaled final : public GDALPamMDArray
      47             : {
      48             :   private:
      49             :     std::shared_ptr<GDALMDArray> m_poParent{};
      50             :     const GDALExtendedDataType m_dt;
      51             :     bool m_bHasNoData;
      52             :     const double m_dfScale;
      53             :     const double m_dfOffset;
      54             :     std::vector<GByte> m_abyRawNoData{};
      55             : 
      56             :   protected:
      57          13 :     explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
      58             :                                  double dfScale, double dfOffset,
      59             :                                  double dfOverriddenDstNodata, GDALDataType eDT)
      60          26 :         : GDALAbstractMDArray(std::string(),
      61          26 :                               "Unscaled view of " + poParent->GetFullName()),
      62             :           GDALPamMDArray(
      63          26 :               std::string(), "Unscaled view of " + poParent->GetFullName(),
      64          26 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
      65          13 :           m_poParent(std::move(poParent)),
      66             :           m_dt(GDALExtendedDataType::Create(eDT)),
      67          13 :           m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
      68          78 :           m_dfScale(dfScale), m_dfOffset(dfOffset)
      69             :     {
      70          13 :         m_abyRawNoData.resize(m_dt.GetSize());
      71             :         const auto eNonComplexDT =
      72          13 :             GDALGetNonComplexDataType(m_dt.GetNumericDataType());
      73          26 :         GDALCopyWords64(
      74          13 :             &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
      75             :             eNonComplexDT, GDALGetDataTypeSizeBytes(eNonComplexDT),
      76          13 :             GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
      77          13 :     }
      78             : 
      79             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
      80             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      81             :                const GDALExtendedDataType &bufferDataType,
      82             :                void *pDstBuffer) const override;
      83             : 
      84             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
      85             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      86             :                 const GDALExtendedDataType &bufferDataType,
      87             :                 const void *pSrcBuffer) override;
      88             : 
      89           0 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
      90             :                      CSLConstList papszOptions) const override
      91             :     {
      92           0 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
      93             :     }
      94             : 
      95             :   public:
      96             :     static std::shared_ptr<GDALMDArrayUnscaled>
      97          13 :     Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
      98             :            double dfOffset, double dfDstNodata, GDALDataType eDT)
      99             :     {
     100             :         auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
     101          13 :             poParent, dfScale, dfOffset, dfDstNodata, eDT)));
     102          13 :         newAr->SetSelf(newAr);
     103          13 :         return newAr;
     104             :     }
     105             : 
     106           1 :     bool IsWritable() const override
     107             :     {
     108           1 :         return m_poParent->IsWritable();
     109             :     }
     110             : 
     111          15 :     const std::string &GetFilename() const override
     112             :     {
     113          15 :         return m_poParent->GetFilename();
     114             :     }
     115             : 
     116             :     const std::vector<std::shared_ptr<GDALDimension>> &
     117         220 :     GetDimensions() const override
     118             :     {
     119         220 :         return m_poParent->GetDimensions();
     120             :     }
     121             : 
     122         103 :     const GDALExtendedDataType &GetDataType() const override
     123             :     {
     124         103 :         return m_dt;
     125             :     }
     126             : 
     127           1 :     const std::string &GetUnit() const override
     128             :     {
     129           1 :         return m_poParent->GetUnit();
     130             :     }
     131             : 
     132           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
     133             :     {
     134           1 :         return m_poParent->GetSpatialRef();
     135             :     }
     136             : 
     137           6 :     const void *GetRawNoDataValue() const override
     138             :     {
     139           6 :         return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
     140             :     }
     141             : 
     142           1 :     bool SetRawNoDataValue(const void *pRawNoData) override
     143             :     {
     144           1 :         m_bHasNoData = true;
     145           1 :         memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
     146           1 :         return true;
     147             :     }
     148             : 
     149           4 :     std::vector<GUInt64> GetBlockSize() const override
     150             :     {
     151           4 :         return m_poParent->GetBlockSize();
     152             :     }
     153             : 
     154             :     std::shared_ptr<GDALAttribute>
     155           0 :     GetAttribute(const std::string &osName) const override
     156             :     {
     157           0 :         return m_poParent->GetAttribute(osName);
     158             :     }
     159             : 
     160             :     std::vector<std::shared_ptr<GDALAttribute>>
     161           1 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
     162             :     {
     163           1 :         return m_poParent->GetAttributes(papszOptions);
     164             :     }
     165             : 
     166           0 :     bool SetUnit(const std::string &osUnit) override
     167             :     {
     168           0 :         return m_poParent->SetUnit(osUnit);
     169             :     }
     170             : 
     171           0 :     bool SetSpatialRef(const OGRSpatialReference *poSRS) override
     172             :     {
     173           0 :         return m_poParent->SetSpatialRef(poSRS);
     174             :     }
     175             : 
     176             :     std::shared_ptr<GDALAttribute>
     177           1 :     CreateAttribute(const std::string &osName,
     178             :                     const std::vector<GUInt64> &anDimensions,
     179             :                     const GDALExtendedDataType &oDataType,
     180             :                     CSLConstList papszOptions = nullptr) override
     181             :     {
     182           1 :         return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
     183           1 :                                            papszOptions);
     184             :     }
     185             : };
     186             : 
     187             : /************************************************************************/
     188             : /*                         ~GDALIHasAttribute()                         */
     189             : /************************************************************************/
     190             : 
     191             : GDALIHasAttribute::~GDALIHasAttribute() = default;
     192             : 
     193             : /************************************************************************/
     194             : /*                            GetAttribute()                            */
     195             : /************************************************************************/
     196             : 
     197             : /** Return an attribute by its name.
     198             :  *
     199             :  * If the attribute does not exist, nullptr should be silently returned.
     200             :  *
     201             :  * @note Driver implementation: this method will fallback to
     202             :  * GetAttributeFromAttributes() is not explicitly implemented
     203             :  *
     204             :  * Drivers known to implement it for groups and arrays: MEM, netCDF.
     205             :  *
     206             :  * This is the same as the C function GDALGroupGetAttribute() or
     207             :  * GDALMDArrayGetAttribute().
     208             :  *
     209             :  * @param osName Attribute name
     210             :  * @return the attribute, or nullptr if it does not exist or an error occurred.
     211             :  */
     212             : std::shared_ptr<GDALAttribute>
     213        2144 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
     214             : {
     215        2144 :     return GetAttributeFromAttributes(osName);
     216             : }
     217             : 
     218             : /************************************************************************/
     219             : /*                       GetAttributeFromAttributes()                   */
     220             : /************************************************************************/
     221             : 
     222             : /** Possible fallback implementation for GetAttribute() using GetAttributes().
     223             :  */
     224             : std::shared_ptr<GDALAttribute>
     225        2144 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
     226             : {
     227        4288 :     auto attrs(GetAttributes());
     228       14813 :     for (const auto &attr : attrs)
     229             :     {
     230       14125 :         if (attr->GetName() == osName)
     231        1456 :             return attr;
     232             :     }
     233         688 :     return nullptr;
     234             : }
     235             : 
     236             : /************************************************************************/
     237             : /*                           GetAttributes()                            */
     238             : /************************************************************************/
     239             : 
     240             : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
     241             :  *
     242             :  * If the attribute does not exist, nullptr should be silently returned.
     243             :  *
     244             :  * @note Driver implementation: optionally implemented. If implemented,
     245             :  * GetAttribute() should also be implemented.
     246             :  *
     247             :  * Drivers known to implement it for groups and arrays: MEM, netCDF.
     248             :  *
     249             :  * This is the same as the C function GDALGroupGetAttributes() or
     250             :  * GDALMDArrayGetAttributes().
     251             : 
     252             :  * @param papszOptions Driver specific options determining how attributes
     253             :  * should be retrieved. Pass nullptr for default behavior.
     254             :  *
     255             :  * @return the attributes.
     256             :  */
     257             : std::vector<std::shared_ptr<GDALAttribute>>
     258          54 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
     259             : {
     260          54 :     return {};
     261             : }
     262             : 
     263             : /************************************************************************/
     264             : /*                             CreateAttribute()                         */
     265             : /************************************************************************/
     266             : 
     267             : /** Create an attribute within a GDALMDArray or GDALGroup.
     268             :  *
     269             :  * The attribute might not be "physically" created until a value is written
     270             :  * into it.
     271             :  *
     272             :  * Optionally implemented.
     273             :  *
     274             :  * Drivers known to implement it: MEM, netCDF
     275             :  *
     276             :  * This is the same as the C function GDALGroupCreateAttribute() or
     277             :  * GDALMDArrayCreateAttribute()
     278             :  *
     279             :  * @param osName Attribute name.
     280             :  * @param anDimensions List of dimension sizes, ordered from the slowest varying
     281             :  *                     dimension first to the fastest varying dimension last.
     282             :  *                     Empty for a scalar attribute (common case)
     283             :  * @param oDataType  Attribute data type.
     284             :  * @param papszOptions Driver specific options determining how the attribute.
     285             :  * should be created.
     286             :  *
     287             :  * @return the new attribute, or nullptr if case of error
     288             :  */
     289           0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
     290             :     CPL_UNUSED const std::string &osName,
     291             :     CPL_UNUSED const std::vector<GUInt64> &anDimensions,
     292             :     CPL_UNUSED const GDALExtendedDataType &oDataType,
     293             :     CPL_UNUSED CSLConstList papszOptions)
     294             : {
     295           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     296             :              "CreateAttribute() not implemented");
     297           0 :     return nullptr;
     298             : }
     299             : 
     300             : /************************************************************************/
     301             : /*                          DeleteAttribute()                           */
     302             : /************************************************************************/
     303             : 
     304             : /** Delete an attribute from a GDALMDArray or GDALGroup.
     305             :  *
     306             :  * Optionally implemented.
     307             :  *
     308             :  * After this call, if a previously obtained instance of the deleted object
     309             :  * is still alive, no method other than for freeing it should be invoked.
     310             :  *
     311             :  * Drivers known to implement it: MEM, netCDF
     312             :  *
     313             :  * This is the same as the C function GDALGroupDeleteAttribute() or
     314             :  * GDALMDArrayDeleteAttribute()
     315             :  *
     316             :  * @param osName Attribute name.
     317             :  * @param papszOptions Driver specific options determining how the attribute.
     318             :  * should be deleted.
     319             :  *
     320             :  * @return true in case of success
     321             :  * @since GDAL 3.8
     322             :  */
     323           0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
     324             :                                         CPL_UNUSED CSLConstList papszOptions)
     325             : {
     326           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     327             :              "DeleteAttribute() not implemented");
     328           0 :     return false;
     329             : }
     330             : 
     331             : /************************************************************************/
     332             : /*                            GDALGroup()                               */
     333             : /************************************************************************/
     334             : 
     335             : //! @cond Doxygen_Suppress
     336        7299 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
     337        7299 :                      const std::string &osContext)
     338        7298 :     : m_osName(osParentName.empty() ? "/" : osName),
     339             :       m_osFullName(
     340       14597 :           !osParentName.empty()
     341       11245 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
     342             :               : "/"),
     343       18545 :       m_osContext(osContext)
     344             : {
     345        7298 : }
     346             : 
     347             : //! @endcond
     348             : 
     349             : /************************************************************************/
     350             : /*                            ~GDALGroup()                              */
     351             : /************************************************************************/
     352             : 
     353             : GDALGroup::~GDALGroup() = default;
     354             : 
     355             : /************************************************************************/
     356             : /*                          GetMDArrayNames()                           */
     357             : /************************************************************************/
     358             : 
     359             : /** Return the list of multidimensional array names contained in this group.
     360             :  *
     361             :  * @note Driver implementation: optionally implemented. If implemented,
     362             :  * OpenMDArray() should also be implemented.
     363             :  *
     364             :  * Drivers known to implement it: MEM, netCDF.
     365             :  *
     366             :  * This is the same as the C function GDALGroupGetMDArrayNames().
     367             :  *
     368             :  * @param papszOptions Driver specific options determining how arrays
     369             :  * should be retrieved. Pass nullptr for default behavior.
     370             :  *
     371             :  * @return the array names.
     372             :  */
     373             : std::vector<std::string>
     374           0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
     375             : {
     376           0 :     return {};
     377             : }
     378             : 
     379             : /************************************************************************/
     380             : /*                     GetMDArrayFullNamesRecursive()                   */
     381             : /************************************************************************/
     382             : 
     383             : /** Return the list of multidimensional array full names contained in this
     384             :  * group and its subgroups.
     385             :  *
     386             :  * This is the same as the C function GDALGroupGetMDArrayFullNamesRecursive().
     387             :  *
     388             :  * @param papszGroupOptions Driver specific options determining how groups
     389             :  * should be retrieved. Pass nullptr for default behavior.
     390             :  * @param papszArrayOptions Driver specific options determining how arrays
     391             :  * should be retrieved. Pass nullptr for default behavior.
     392             :  *
     393             :  * @return the array full names.
     394             :  *
     395             :  * @since 3.11
     396             :  */
     397             : std::vector<std::string>
     398           8 : GDALGroup::GetMDArrayFullNamesRecursive(CSLConstList papszGroupOptions,
     399             :                                         CSLConstList papszArrayOptions) const
     400             : {
     401           8 :     std::vector<std::string> ret;
     402          16 :     std::list<std::shared_ptr<GDALGroup>> stackGroups;
     403           8 :     stackGroups.push_back(nullptr);  // nullptr means this
     404          19 :     while (!stackGroups.empty())
     405             :     {
     406          22 :         std::shared_ptr<GDALGroup> groupPtr = std::move(stackGroups.front());
     407          11 :         stackGroups.erase(stackGroups.begin());
     408          11 :         const GDALGroup *poCurGroup = groupPtr ? groupPtr.get() : this;
     409          25 :         for (const std::string &arrayName :
     410          61 :              poCurGroup->GetMDArrayNames(papszArrayOptions))
     411             :         {
     412          50 :             std::string osFullName = poCurGroup->GetFullName();
     413          25 :             if (!osFullName.empty() && osFullName.back() != '/')
     414           3 :                 osFullName += '/';
     415          25 :             osFullName += arrayName;
     416          25 :             ret.push_back(std::move(osFullName));
     417             :         }
     418          11 :         auto insertionPoint = stackGroups.begin();
     419           3 :         for (const auto &osSubGroup :
     420          17 :              poCurGroup->GetGroupNames(papszGroupOptions))
     421             :         {
     422           6 :             auto poSubGroup = poCurGroup->OpenGroup(osSubGroup);
     423           3 :             if (poSubGroup)
     424           3 :                 stackGroups.insert(insertionPoint, std::move(poSubGroup));
     425             :         }
     426             :     }
     427             : 
     428          16 :     return ret;
     429             : }
     430             : 
     431             : /************************************************************************/
     432             : /*                            OpenMDArray()                             */
     433             : /************************************************************************/
     434             : 
     435             : /** Open and return a multidimensional array.
     436             :  *
     437             :  * @note Driver implementation: optionally implemented. If implemented,
     438             :  * GetMDArrayNames() should also be implemented.
     439             :  *
     440             :  * Drivers known to implement it: MEM, netCDF.
     441             :  *
     442             :  * This is the same as the C function GDALGroupOpenMDArray().
     443             :  *
     444             :  * @param osName Array name.
     445             :  * @param papszOptions Driver specific options determining how the array should
     446             :  * be opened.  Pass nullptr for default behavior.
     447             :  *
     448             :  * @return the array, or nullptr.
     449             :  */
     450             : std::shared_ptr<GDALMDArray>
     451           0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
     452             :                        CPL_UNUSED CSLConstList papszOptions) const
     453             : {
     454           0 :     return nullptr;
     455             : }
     456             : 
     457             : /************************************************************************/
     458             : /*                           GetGroupNames()                            */
     459             : /************************************************************************/
     460             : 
     461             : /** Return the list of sub-groups contained in this group.
     462             :  *
     463             :  * @note Driver implementation: optionally implemented. If implemented,
     464             :  * OpenGroup() should also be implemented.
     465             :  *
     466             :  * Drivers known to implement it: MEM, netCDF.
     467             :  *
     468             :  * This is the same as the C function GDALGroupGetGroupNames().
     469             :  *
     470             :  * @param papszOptions Driver specific options determining how groups
     471             :  * should be retrieved. Pass nullptr for default behavior.
     472             :  *
     473             :  * @return the group names.
     474             :  */
     475             : std::vector<std::string>
     476           4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
     477             : {
     478           4 :     return {};
     479             : }
     480             : 
     481             : /************************************************************************/
     482             : /*                             OpenGroup()                              */
     483             : /************************************************************************/
     484             : 
     485             : /** Open and return a sub-group.
     486             :  *
     487             :  * @note Driver implementation: optionally implemented. If implemented,
     488             :  * GetGroupNames() should also be implemented.
     489             :  *
     490             :  * Drivers known to implement it: MEM, netCDF.
     491             :  *
     492             :  * This is the same as the C function GDALGroupOpenGroup().
     493             :  *
     494             :  * @param osName Sub-group name.
     495             :  * @param papszOptions Driver specific options determining how the sub-group
     496             :  * should be opened.  Pass nullptr for default behavior.
     497             :  *
     498             :  * @return the group, or nullptr.
     499             :  */
     500             : std::shared_ptr<GDALGroup>
     501           4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
     502             :                      CPL_UNUSED CSLConstList papszOptions) const
     503             : {
     504           4 :     return nullptr;
     505             : }
     506             : 
     507             : /************************************************************************/
     508             : /*                        GetVectorLayerNames()                         */
     509             : /************************************************************************/
     510             : 
     511             : /** Return the list of layer names contained in this group.
     512             :  *
     513             :  * @note Driver implementation: optionally implemented. If implemented,
     514             :  * OpenVectorLayer() should also be implemented.
     515             :  *
     516             :  * Drivers known to implement it: OpenFileGDB, FileGDB
     517             :  *
     518             :  * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
     519             :  * GDALDataset::GetLayer() should then be used.
     520             :  *
     521             :  * This is the same as the C function GDALGroupGetVectorLayerNames().
     522             :  *
     523             :  * @param papszOptions Driver specific options determining how layers
     524             :  * should be retrieved. Pass nullptr for default behavior.
     525             :  *
     526             :  * @return the vector layer names.
     527             :  * @since GDAL 3.4
     528             :  */
     529             : std::vector<std::string>
     530           1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
     531             : {
     532           1 :     return {};
     533             : }
     534             : 
     535             : /************************************************************************/
     536             : /*                           OpenVectorLayer()                          */
     537             : /************************************************************************/
     538             : 
     539             : /** Open and return a vector layer.
     540             :  *
     541             :  * Due to the historical ownership of OGRLayer* by GDALDataset*, the
     542             :  * lifetime of the returned OGRLayer* is linked to the one of the owner
     543             :  * dataset (contrary to the general design of this class where objects can be
     544             :  * used independently of the object that returned them)
     545             :  *
     546             :  * @note Driver implementation: optionally implemented. If implemented,
     547             :  * GetVectorLayerNames() should also be implemented.
     548             :  *
     549             :  * Drivers known to implement it: MEM, netCDF.
     550             :  *
     551             :  * This is the same as the C function GDALGroupOpenVectorLayer().
     552             :  *
     553             :  * @param osName Vector layer name.
     554             :  * @param papszOptions Driver specific options determining how the layer should
     555             :  * be opened.  Pass nullptr for default behavior.
     556             :  *
     557             :  * @return the group, or nullptr.
     558             :  */
     559           2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
     560             :                                      CPL_UNUSED CSLConstList papszOptions) const
     561             : {
     562           2 :     return nullptr;
     563             : }
     564             : 
     565             : /************************************************************************/
     566             : /*                             GetDimensions()                          */
     567             : /************************************************************************/
     568             : 
     569             : /** Return the list of dimensions contained in this group and used by its
     570             :  * arrays.
     571             :  *
     572             :  * This is for dimensions that can potentially be used by several arrays.
     573             :  * Not all drivers might implement this. To retrieve the dimensions used by
     574             :  * a specific array, use GDALMDArray::GetDimensions().
     575             :  *
     576             :  * Drivers known to implement it: MEM, netCDF
     577             :  *
     578             :  * This is the same as the C function GDALGroupGetDimensions().
     579             :  *
     580             :  * @param papszOptions Driver specific options determining how groups
     581             :  * should be retrieved. Pass nullptr for default behavior.
     582             :  *
     583             :  * @return the dimensions.
     584             :  */
     585             : std::vector<std::shared_ptr<GDALDimension>>
     586          11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
     587             : {
     588          11 :     return {};
     589             : }
     590             : 
     591             : /************************************************************************/
     592             : /*                         GetStructuralInfo()                          */
     593             : /************************************************************************/
     594             : 
     595             : /** Return structural information on the group.
     596             :  *
     597             :  * This may be the compression, etc..
     598             :  *
     599             :  * The return value should not be freed and is valid until GDALGroup is
     600             :  * released or this function called again.
     601             :  *
     602             :  * This is the same as the C function GDALGroupGetStructuralInfo().
     603             :  */
     604          42 : CSLConstList GDALGroup::GetStructuralInfo() const
     605             : {
     606          42 :     return nullptr;
     607             : }
     608             : 
     609             : /************************************************************************/
     610             : /*                              CreateGroup()                           */
     611             : /************************************************************************/
     612             : 
     613             : /** Create a sub-group within a group.
     614             :  *
     615             :  * Optionally implemented by drivers.
     616             :  *
     617             :  * Drivers known to implement it: MEM, netCDF
     618             :  *
     619             :  * This is the same as the C function GDALGroupCreateGroup().
     620             :  *
     621             :  * @param osName Sub-group name.
     622             :  * @param papszOptions Driver specific options determining how the sub-group
     623             :  * should be created.
     624             :  *
     625             :  * @return the new sub-group, or nullptr in case of error.
     626             :  */
     627             : std::shared_ptr<GDALGroup>
     628           0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
     629             :                        CPL_UNUSED CSLConstList papszOptions)
     630             : {
     631           0 :     CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
     632           0 :     return nullptr;
     633             : }
     634             : 
     635             : /************************************************************************/
     636             : /*                          DeleteGroup()                               */
     637             : /************************************************************************/
     638             : 
     639             : /** Delete a sub-group from a group.
     640             :  *
     641             :  * Optionally implemented.
     642             :  *
     643             :  * After this call, if a previously obtained instance of the deleted object
     644             :  * is still alive, no method other than for freeing it should be invoked.
     645             :  *
     646             :  * Drivers known to implement it: MEM, Zarr
     647             :  *
     648             :  * This is the same as the C function GDALGroupDeleteGroup().
     649             :  *
     650             :  * @param osName Sub-group name.
     651             :  * @param papszOptions Driver specific options determining how the group.
     652             :  * should be deleted.
     653             :  *
     654             :  * @return true in case of success
     655             :  * @since GDAL 3.8
     656             :  */
     657           0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
     658             :                             CPL_UNUSED CSLConstList papszOptions)
     659             : {
     660           0 :     CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
     661           0 :     return false;
     662             : }
     663             : 
     664             : /************************************************************************/
     665             : /*                            CreateDimension()                         */
     666             : /************************************************************************/
     667             : 
     668             : /** Create a dimension within a group.
     669             :  *
     670             :  * @note Driver implementation: drivers supporting CreateDimension() should
     671             :  * implement this method, but do not have necessarily to implement
     672             :  * GDALGroup::GetDimensions().
     673             :  *
     674             :  * Drivers known to implement it: MEM, netCDF
     675             :  *
     676             :  * This is the same as the C function GDALGroupCreateDimension().
     677             :  *
     678             :  * @param osName Dimension name.
     679             :  * @param osType Dimension type (might be empty, and ignored by drivers)
     680             :  * @param osDirection Dimension direction (might be empty, and ignored by
     681             :  * drivers)
     682             :  * @param nSize  Number of values indexed by this dimension. Should be > 0.
     683             :  * @param papszOptions Driver specific options determining how the dimension
     684             :  * should be created.
     685             :  *
     686             :  * @return the new dimension, or nullptr if case of error
     687             :  */
     688           0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
     689             :     CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
     690             :     CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
     691             :     CPL_UNUSED CSLConstList papszOptions)
     692             : {
     693           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     694             :              "CreateDimension() not implemented");
     695           0 :     return nullptr;
     696             : }
     697             : 
     698             : /************************************************************************/
     699             : /*                             CreateMDArray()                          */
     700             : /************************************************************************/
     701             : 
     702             : /** Create a multidimensional array within a group.
     703             :  *
     704             :  * It is recommended that the GDALDimension objects passed in aoDimensions
     705             :  * belong to this group, either by retrieving them with GetDimensions()
     706             :  * or creating a new one with CreateDimension().
     707             :  *
     708             :  * Optionally implemented.
     709             :  *
     710             :  * Drivers known to implement it: MEM, netCDF
     711             :  *
     712             :  * This is the same as the C function GDALGroupCreateMDArray().
     713             :  *
     714             :  * @note Driver implementation: drivers should take into account the possibility
     715             :  * that GDALDimension object passed in aoDimensions might belong to a different
     716             :  * group / dataset / driver and act accordingly.
     717             :  *
     718             :  * @param osName Array name.
     719             :  * @param aoDimensions List of dimensions, ordered from the slowest varying
     720             :  *                     dimension first to the fastest varying dimension last.
     721             :  *                     Might be empty for a scalar array (if supported by
     722             :  * driver)
     723             :  * @param oDataType  Array data type.
     724             :  * @param papszOptions Driver specific options determining how the array
     725             :  * should be created.
     726             :  *
     727             :  * @return the new array, or nullptr in case of error
     728             :  */
     729           0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
     730             :     CPL_UNUSED const std::string &osName,
     731             :     CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
     732             :     CPL_UNUSED const GDALExtendedDataType &oDataType,
     733             :     CPL_UNUSED CSLConstList papszOptions)
     734             : {
     735           0 :     CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
     736           0 :     return nullptr;
     737             : }
     738             : 
     739             : /************************************************************************/
     740             : /*                          DeleteMDArray()                             */
     741             : /************************************************************************/
     742             : 
     743             : /** Delete an array from a group.
     744             :  *
     745             :  * Optionally implemented.
     746             :  *
     747             :  * After this call, if a previously obtained instance of the deleted object
     748             :  * is still alive, no method other than for freeing it should be invoked.
     749             :  *
     750             :  * Drivers known to implement it: MEM, Zarr
     751             :  *
     752             :  * This is the same as the C function GDALGroupDeleteMDArray().
     753             :  *
     754             :  * @param osName Arrayname.
     755             :  * @param papszOptions Driver specific options determining how the array.
     756             :  * should be deleted.
     757             :  *
     758             :  * @return true in case of success
     759             :  * @since GDAL 3.8
     760             :  */
     761           0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
     762             :                               CPL_UNUSED CSLConstList papszOptions)
     763             : {
     764           0 :     CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
     765           0 :     return false;
     766             : }
     767             : 
     768             : /************************************************************************/
     769             : /*                           GetTotalCopyCost()                         */
     770             : /************************************************************************/
     771             : 
     772             : /** Return a total "cost" to copy the group.
     773             :  *
     774             :  * Used as a parameter for CopFrom()
     775             :  */
     776          32 : GUInt64 GDALGroup::GetTotalCopyCost() const
     777             : {
     778          32 :     GUInt64 nCost = COPY_COST;
     779          32 :     nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
     780             : 
     781          64 :     auto groupNames = GetGroupNames();
     782          38 :     for (const auto &name : groupNames)
     783             :     {
     784          12 :         auto subGroup = OpenGroup(name);
     785           6 :         if (subGroup)
     786             :         {
     787           6 :             nCost += subGroup->GetTotalCopyCost();
     788             :         }
     789             :     }
     790             : 
     791          32 :     auto arrayNames = GetMDArrayNames();
     792          98 :     for (const auto &name : arrayNames)
     793             :     {
     794         132 :         auto array = OpenMDArray(name);
     795          66 :         if (array)
     796             :         {
     797          66 :             nCost += array->GetTotalCopyCost();
     798             :         }
     799             :     }
     800          64 :     return nCost;
     801             : }
     802             : 
     803             : /************************************************************************/
     804             : /*                               CopyFrom()                             */
     805             : /************************************************************************/
     806             : 
     807             : /** Copy the content of a group into a new (generally empty) group.
     808             :  *
     809             :  * @param poDstRootGroup Destination root group. Must NOT be nullptr.
     810             :  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
     811             :  *                   of some output drivers this is not recommended)
     812             :  * @param poSrcGroup Source group. Must NOT be nullptr.
     813             :  * @param bStrict Whether to enable strict mode. In strict mode, any error will
     814             :  *                stop the copy. In relaxed mode, the copy will be attempted to
     815             :  *                be pursued.
     816             :  * @param nCurCost  Should be provided as a variable initially set to 0.
     817             :  * @param nTotalCost Total cost from GetTotalCopyCost().
     818             :  * @param pfnProgress Progress callback, or nullptr.
     819             :  * @param pProgressData Progress user data, or nulptr.
     820             :  * @param papszOptions Creation options. Currently, only array creation
     821             :  *                     options are supported. They must be prefixed with
     822             :  * "ARRAY:" . The scope may be further restricted to arrays of a certain
     823             :  *                     dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
     824             :  *                     For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
     825             :  *                     restrict BLOCKSIZE=256,256 to arrays of dimension 2.
     826             :  *                     Restriction to arrays of a given name is done with adding
     827             :  *                     "IF(NAME={name}):" after "ARRAY:". {name} can also be
     828             :  *                     a full qualified name.
     829             :  *                     A non-driver specific ARRAY option, "AUTOSCALE=YES" can
     830             :  * be used to ask (non indexing) variables of type Float32 or Float64 to be
     831             :  * scaled to UInt16 with scale and offset values being computed from the minimum
     832             :  * and maximum of the source array. The integer data type used can be set with
     833             :  *                     AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
     834             :  *
     835             :  * @return true in case of success (or partial success if bStrict == false).
     836             :  */
     837          32 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
     838             :                          GDALDataset *poSrcDS,
     839             :                          const std::shared_ptr<GDALGroup> &poSrcGroup,
     840             :                          bool bStrict, GUInt64 &nCurCost,
     841             :                          const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
     842             :                          void *pProgressData, CSLConstList papszOptions)
     843             : {
     844          32 :     if (pfnProgress == nullptr)
     845           0 :         pfnProgress = GDALDummyProgress;
     846             : 
     847             : #define EXIT_OR_CONTINUE_IF_NULL(x)                                            \
     848             :     if (!(x))                                                                  \
     849             :     {                                                                          \
     850             :         if (bStrict)                                                           \
     851             :             return false;                                                      \
     852             :         continue;                                                              \
     853             :     }                                                                          \
     854             :     (void)0
     855             : 
     856             :     try
     857             :     {
     858          32 :         nCurCost += GDALGroup::COPY_COST;
     859             : 
     860          64 :         const auto srcDims = poSrcGroup->GetDimensions();
     861             :         std::map<std::string, std::shared_ptr<GDALDimension>>
     862          64 :             mapExistingDstDims;
     863          64 :         std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
     864          84 :         for (const auto &dim : srcDims)
     865             :         {
     866             :             auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
     867          52 :                                           dim->GetDirection(), dim->GetSize());
     868          52 :             EXIT_OR_CONTINUE_IF_NULL(dstDim);
     869          52 :             mapExistingDstDims[dim->GetName()] = std::move(dstDim);
     870         104 :             auto poIndexingVarSrc(dim->GetIndexingVariable());
     871          52 :             if (poIndexingVarSrc)
     872             :             {
     873             :                 mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
     874          33 :                                                        ->GetName()] =
     875          66 :                     dim->GetName();
     876             :             }
     877             :         }
     878             : 
     879          64 :         auto attrs = poSrcGroup->GetAttributes();
     880          46 :         for (const auto &attr : attrs)
     881             :         {
     882             :             auto dstAttr =
     883          14 :                 CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
     884          28 :                                 attr->GetDataType());
     885          14 :             EXIT_OR_CONTINUE_IF_NULL(dstAttr);
     886          14 :             auto raw(attr->ReadAsRaw());
     887          14 :             if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
     888           0 :                 return false;
     889             :         }
     890          32 :         if (!attrs.empty())
     891             :         {
     892           7 :             nCurCost += attrs.size() * GDALAttribute::COPY_COST;
     893           7 :             if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
     894           0 :                 return false;
     895             :         }
     896             : 
     897             :         const auto CopyArray =
     898          66 :             [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
     899             :              &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
     900             :              papszOptions, bStrict, &nCurCost,
     901         590 :              nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
     902             :         {
     903             :             // Map source dimensions to target dimensions
     904         132 :             std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
     905          66 :             const auto &srcArrayDims(srcArray->GetDimensions());
     906         165 :             for (const auto &dim : srcArrayDims)
     907             :             {
     908             :                 auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
     909          99 :                     dim->GetFullName());
     910          99 :                 if (dstDim && dstDim->GetSize() == dim->GetSize())
     911             :                 {
     912          89 :                     dstArrayDims.emplace_back(dstDim);
     913             :                 }
     914             :                 else
     915             :                 {
     916          10 :                     auto oIter = mapExistingDstDims.find(dim->GetName());
     917          19 :                     if (oIter != mapExistingDstDims.end() &&
     918           9 :                         oIter->second->GetSize() == dim->GetSize())
     919             :                     {
     920           8 :                         dstArrayDims.emplace_back(oIter->second);
     921             :                     }
     922             :                     else
     923             :                     {
     924           2 :                         std::string newDimName;
     925           2 :                         if (oIter == mapExistingDstDims.end())
     926             :                         {
     927           1 :                             newDimName = dim->GetName();
     928             :                         }
     929             :                         else
     930             :                         {
     931           1 :                             std::string newDimNamePrefix(srcArray->GetName() +
     932           3 :                                                          '_' + dim->GetName());
     933           1 :                             newDimName = newDimNamePrefix;
     934           1 :                             int nIterCount = 2;
     935           0 :                             while (
     936           1 :                                 cpl::contains(mapExistingDstDims, newDimName))
     937             :                             {
     938           0 :                                 newDimName = newDimNamePrefix +
     939           0 :                                              CPLSPrintf("_%d", nIterCount);
     940           0 :                                 nIterCount++;
     941             :                             }
     942             :                         }
     943           4 :                         dstDim = CreateDimension(newDimName, dim->GetType(),
     944             :                                                  dim->GetDirection(),
     945           4 :                                                  dim->GetSize());
     946           2 :                         if (!dstDim)
     947           0 :                             return false;
     948           2 :                         mapExistingDstDims[newDimName] = dstDim;
     949           2 :                         dstArrayDims.emplace_back(dstDim);
     950             :                     }
     951             :                 }
     952             :             }
     953             : 
     954         132 :             CPLStringList aosArrayCO;
     955          66 :             bool bAutoScale = false;
     956          66 :             GDALDataType eAutoScaleType = GDT_UInt16;
     957          73 :             for (const char *pszItem : cpl::Iterate(papszOptions))
     958             :             {
     959           7 :                 if (STARTS_WITH_CI(pszItem, "ARRAY:"))
     960             :                 {
     961           7 :                     const char *pszOption = pszItem + strlen("ARRAY:");
     962           7 :                     if (STARTS_WITH_CI(pszOption, "IF(DIM="))
     963             :                     {
     964           1 :                         const char *pszNext = strchr(pszOption, ':');
     965           1 :                         if (pszNext != nullptr)
     966             :                         {
     967           1 :                             int nDim = atoi(pszOption + strlen("IF(DIM="));
     968           1 :                             if (static_cast<size_t>(nDim) ==
     969           1 :                                 dstArrayDims.size())
     970             :                             {
     971           1 :                                 pszOption = pszNext + 1;
     972             :                             }
     973             :                             else
     974             :                             {
     975           0 :                                 pszOption = nullptr;
     976             :                             }
     977             :                         }
     978             :                     }
     979           6 :                     else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
     980             :                     {
     981           2 :                         const char *pszName = pszOption + strlen("IF(NAME=");
     982           2 :                         const char *pszNext = strchr(pszName, ':');
     983           2 :                         if (pszNext != nullptr && pszNext > pszName &&
     984           2 :                             pszNext[-1] == ')')
     985             :                         {
     986           4 :                             CPLString osName;
     987           2 :                             osName.assign(pszName, pszNext - pszName - 1);
     988           3 :                             if (osName == srcArray->GetName() ||
     989           1 :                                 osName == srcArray->GetFullName())
     990             :                             {
     991           2 :                                 pszOption = pszNext + 1;
     992             :                             }
     993             :                             else
     994             :                             {
     995           0 :                                 pszOption = nullptr;
     996             :                             }
     997             :                         }
     998             :                     }
     999           7 :                     if (pszOption)
    1000             :                     {
    1001           7 :                         if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
    1002             :                         {
    1003             :                             bAutoScale =
    1004           2 :                                 CPLTestBool(pszOption + strlen("AUTOSCALE="));
    1005             :                         }
    1006           5 :                         else if (STARTS_WITH_CI(pszOption,
    1007             :                                                 "AUTOSCALE_DATA_TYPE="))
    1008             :                         {
    1009           1 :                             const char *pszDataType =
    1010             :                                 pszOption + strlen("AUTOSCALE_DATA_TYPE=");
    1011           1 :                             eAutoScaleType = GDALGetDataTypeByName(pszDataType);
    1012           2 :                             if (GDALDataTypeIsComplex(eAutoScaleType) ||
    1013           1 :                                 GDALDataTypeIsFloating(eAutoScaleType))
    1014             :                             {
    1015           0 :                                 CPLError(CE_Failure, CPLE_NotSupported,
    1016             :                                          "Unsupported value for "
    1017             :                                          "AUTOSCALE_DATA_TYPE");
    1018           0 :                                 return false;
    1019             :                             }
    1020             :                         }
    1021             :                         else
    1022             :                         {
    1023           4 :                             aosArrayCO.AddString(pszOption);
    1024             :                         }
    1025             :                     }
    1026             :                 }
    1027             :             }
    1028             : 
    1029          66 :             if (aosArrayCO.FetchNameValue("BLOCKSIZE") == nullptr)
    1030             :             {
    1031         130 :                 const auto anBlockSize = srcArray->GetBlockSize();
    1032         130 :                 std::string osBlockSize;
    1033         162 :                 for (auto v : anBlockSize)
    1034             :                 {
    1035          97 :                     if (!osBlockSize.empty())
    1036          34 :                         osBlockSize += ',';
    1037          97 :                     osBlockSize += std::to_string(v);
    1038             :                 }
    1039          65 :                 if (!osBlockSize.empty())
    1040          63 :                     aosArrayCO.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
    1041             :             }
    1042             : 
    1043             :             auto oIterDimName =
    1044          66 :                 mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
    1045          66 :             const auto &srcArrayType = srcArray->GetDataType();
    1046             : 
    1047          66 :             std::shared_ptr<GDALMDArray> dstArray;
    1048             : 
    1049             :             // Only autoscale non-indexing variables
    1050          66 :             bool bHasOffset = false;
    1051          66 :             bool bHasScale = false;
    1052           4 :             if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
    1053           4 :                 (srcArrayType.GetNumericDataType() == GDT_Float16 ||
    1054           2 :                  srcArrayType.GetNumericDataType() == GDT_Float32 ||
    1055           0 :                  srcArrayType.GetNumericDataType() == GDT_Float64) &&
    1056           2 :                 srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
    1057          70 :                 srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
    1058          68 :                 oIterDimName == mapSrcVariableNameToIndexedDimName.end())
    1059             :             {
    1060           2 :                 constexpr bool bApproxOK = false;
    1061           2 :                 constexpr bool bForce = true;
    1062           2 :                 double dfMin = 0.0;
    1063           2 :                 double dfMax = 0.0;
    1064           2 :                 if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
    1065             :                                             nullptr, nullptr, nullptr, nullptr,
    1066           2 :                                             nullptr) != CE_None)
    1067             :                 {
    1068           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1069             :                              "Could not retrieve statistics for array %s",
    1070           0 :                              srcArray->GetName().c_str());
    1071           0 :                     return false;
    1072             :                 }
    1073           2 :                 double dfDTMin = 0;
    1074           2 :                 double dfDTMax = 0;
    1075             : #define setDTMinMax(ctype)                                                     \
    1076             :     do                                                                         \
    1077             :     {                                                                          \
    1078             :         dfDTMin = static_cast<double>(cpl::NumericLimits<ctype>::lowest());    \
    1079             :         dfDTMax = static_cast<double>(cpl::NumericLimits<ctype>::max());       \
    1080             :     } while (0)
    1081             : 
    1082           2 :                 switch (eAutoScaleType)
    1083             :                 {
    1084           0 :                     case GDT_Byte:
    1085           0 :                         setDTMinMax(GByte);
    1086           0 :                         break;
    1087           0 :                     case GDT_Int8:
    1088           0 :                         setDTMinMax(GInt8);
    1089           0 :                         break;
    1090           1 :                     case GDT_UInt16:
    1091           1 :                         setDTMinMax(GUInt16);
    1092           1 :                         break;
    1093           1 :                     case GDT_Int16:
    1094           1 :                         setDTMinMax(GInt16);
    1095           1 :                         break;
    1096           0 :                     case GDT_UInt32:
    1097           0 :                         setDTMinMax(GUInt32);
    1098           0 :                         break;
    1099           0 :                     case GDT_Int32:
    1100           0 :                         setDTMinMax(GInt32);
    1101           0 :                         break;
    1102           0 :                     case GDT_UInt64:
    1103           0 :                         setDTMinMax(std::uint64_t);
    1104           0 :                         break;
    1105           0 :                     case GDT_Int64:
    1106           0 :                         setDTMinMax(std::int64_t);
    1107           0 :                         break;
    1108           0 :                     case GDT_Float16:
    1109             :                     case GDT_Float32:
    1110             :                     case GDT_Float64:
    1111             :                     case GDT_Unknown:
    1112             :                     case GDT_CInt16:
    1113             :                     case GDT_CInt32:
    1114             :                     case GDT_CFloat16:
    1115             :                     case GDT_CFloat32:
    1116             :                     case GDT_CFloat64:
    1117             :                     case GDT_TypeCount:
    1118           0 :                         CPLAssert(false);
    1119             :                 }
    1120             : 
    1121             :                 dstArray =
    1122           4 :                     CreateMDArray(srcArray->GetName(), dstArrayDims,
    1123           4 :                                   GDALExtendedDataType::Create(eAutoScaleType),
    1124           4 :                                   aosArrayCO.List());
    1125           2 :                 if (!dstArray)
    1126           0 :                     return !bStrict;
    1127             : 
    1128           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1129             :                 {
    1130             :                     // If there's a nodata value in the source array, reserve
    1131             :                     // DTMax for that purpose in the target scaled array
    1132           1 :                     if (!dstArray->SetNoDataValue(dfDTMax))
    1133             :                     {
    1134           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    1135             :                                  "Cannot set nodata value");
    1136           0 :                         return false;
    1137             :                     }
    1138           1 :                     dfDTMax--;
    1139             :                 }
    1140           2 :                 const double dfScale =
    1141           2 :                     dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
    1142           2 :                 const double dfOffset = dfMin - dfDTMin * dfScale;
    1143             : 
    1144           4 :                 if (!dstArray->SetOffset(dfOffset) ||
    1145           2 :                     !dstArray->SetScale(dfScale))
    1146             :                 {
    1147           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1148             :                              "Cannot set scale/offset");
    1149           0 :                     return false;
    1150             :                 }
    1151             : 
    1152           2 :                 auto poUnscaled = dstArray->GetUnscaled();
    1153           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1154             :                 {
    1155           1 :                     poUnscaled->SetNoDataValue(
    1156             :                         srcArray->GetNoDataValueAsDouble());
    1157             :                 }
    1158             : 
    1159             :                 // Copy source array into unscaled array
    1160           4 :                 if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1161             :                                           nCurCost, nTotalCost, pfnProgress,
    1162           2 :                                           pProgressData))
    1163             :                 {
    1164           0 :                     return false;
    1165             :                 }
    1166             :             }
    1167             :             else
    1168             :             {
    1169         128 :                 dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
    1170         128 :                                          srcArrayType, aosArrayCO.List());
    1171          64 :                 if (!dstArray)
    1172           0 :                     return !bStrict;
    1173             : 
    1174         128 :                 if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1175             :                                         nCurCost, nTotalCost, pfnProgress,
    1176          64 :                                         pProgressData))
    1177             :                 {
    1178           0 :                     return false;
    1179             :                 }
    1180             :             }
    1181             : 
    1182             :             // If this array is the indexing variable of a dimension, link them
    1183             :             // together.
    1184          66 :             if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
    1185             :             {
    1186             :                 auto oCorrespondingDimIter =
    1187          33 :                     mapExistingDstDims.find(oIterDimName->second);
    1188          33 :                 if (oCorrespondingDimIter != mapExistingDstDims.end())
    1189             :                 {
    1190             :                     CPLErrorStateBackuper oErrorStateBackuper(
    1191          33 :                         CPLQuietErrorHandler);
    1192          66 :                     oCorrespondingDimIter->second->SetIndexingVariable(
    1193          33 :                         std::move(dstArray));
    1194             :                 }
    1195             :             }
    1196             : 
    1197          66 :             return true;
    1198          32 :         };
    1199             : 
    1200          64 :         const auto arrayNames = poSrcGroup->GetMDArrayNames();
    1201             : 
    1202             :         // Start by copying arrays that are indexing variables of dimensions
    1203          98 :         for (const auto &name : arrayNames)
    1204             :         {
    1205          66 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1206          66 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1207             : 
    1208          66 :             if (cpl::contains(mapSrcVariableNameToIndexedDimName,
    1209          66 :                               srcArray->GetName()))
    1210             :             {
    1211          33 :                 if (!CopyArray(srcArray))
    1212           0 :                     return false;
    1213             :             }
    1214             :         }
    1215             : 
    1216             :         // Then copy regular arrays
    1217          98 :         for (const auto &name : arrayNames)
    1218             :         {
    1219          66 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1220          66 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1221             : 
    1222          66 :             if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
    1223          66 :                                srcArray->GetName()))
    1224             :             {
    1225          33 :                 if (!CopyArray(srcArray))
    1226           0 :                     return false;
    1227             :             }
    1228             :         }
    1229             : 
    1230          64 :         const auto groupNames = poSrcGroup->GetGroupNames();
    1231          38 :         for (const auto &name : groupNames)
    1232             :         {
    1233           6 :             auto srcSubGroup = poSrcGroup->OpenGroup(name);
    1234           6 :             EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
    1235           6 :             auto dstSubGroup = CreateGroup(name);
    1236           6 :             EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
    1237          12 :             if (!dstSubGroup->CopyFrom(
    1238             :                     poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
    1239           6 :                     nTotalCost, pfnProgress, pProgressData, papszOptions))
    1240           0 :                 return false;
    1241             :         }
    1242             : 
    1243          32 :         if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    1244           0 :             return false;
    1245             : 
    1246          32 :         return true;
    1247             :     }
    1248           0 :     catch (const std::exception &e)
    1249             :     {
    1250           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1251           0 :         return false;
    1252             :     }
    1253             : }
    1254             : 
    1255             : /************************************************************************/
    1256             : /*                         GetInnerMostGroup()                          */
    1257             : /************************************************************************/
    1258             : 
    1259             : //! @cond Doxygen_Suppress
    1260             : const GDALGroup *
    1261        1452 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
    1262             :                              std::shared_ptr<GDALGroup> &curGroupHolder,
    1263             :                              std::string &osLastPart) const
    1264             : {
    1265        1452 :     if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
    1266           6 :         return nullptr;
    1267        1446 :     const GDALGroup *poCurGroup = this;
    1268             :     CPLStringList aosTokens(
    1269        2892 :         CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
    1270        1446 :     if (aosTokens.size() == 0)
    1271             :     {
    1272           0 :         return nullptr;
    1273             :     }
    1274             : 
    1275        1784 :     for (int i = 0; i < aosTokens.size() - 1; i++)
    1276             :     {
    1277         350 :         curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
    1278         350 :         if (!curGroupHolder)
    1279             :         {
    1280          12 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
    1281             :                      aosTokens[i]);
    1282          12 :             return nullptr;
    1283             :         }
    1284         338 :         poCurGroup = curGroupHolder.get();
    1285             :     }
    1286        1434 :     osLastPart = aosTokens[aosTokens.size() - 1];
    1287        1434 :     return poCurGroup;
    1288             : }
    1289             : 
    1290             : //! @endcond
    1291             : 
    1292             : /************************************************************************/
    1293             : /*                      OpenMDArrayFromFullname()                       */
    1294             : /************************************************************************/
    1295             : 
    1296             : /** Get an array from its fully qualified name */
    1297             : std::shared_ptr<GDALMDArray>
    1298         676 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
    1299             :                                    CSLConstList papszOptions) const
    1300             : {
    1301        1352 :     std::string osName;
    1302         676 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1303         676 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1304         676 :     if (poGroup == nullptr)
    1305          12 :         return nullptr;
    1306         664 :     return poGroup->OpenMDArray(osName, papszOptions);
    1307             : }
    1308             : 
    1309             : /************************************************************************/
    1310             : /*                      OpenAttributeFromFullname()                     */
    1311             : /************************************************************************/
    1312             : 
    1313             : /** Get an attribute from its fully qualified name */
    1314             : std::shared_ptr<GDALAttribute>
    1315           9 : GDALGroup::OpenAttributeFromFullname(const std::string &osFullName,
    1316             :                                      CSLConstList papszOptions) const
    1317             : {
    1318           9 :     const auto pos = osFullName.rfind('/');
    1319           9 :     if (pos == std::string::npos)
    1320           0 :         return nullptr;
    1321          18 :     const std::string attrName = osFullName.substr(pos + 1);
    1322           9 :     if (pos == 0)
    1323           2 :         return GetAttribute(attrName);
    1324          14 :     const std::string container = osFullName.substr(0, pos);
    1325          14 :     auto poArray = OpenMDArrayFromFullname(container, papszOptions);
    1326           7 :     if (poArray)
    1327           4 :         return poArray->GetAttribute(attrName);
    1328           6 :     auto poGroup = OpenGroupFromFullname(container, papszOptions);
    1329           3 :     if (poGroup)
    1330           1 :         return poGroup->GetAttribute(attrName);
    1331           2 :     return nullptr;
    1332             : }
    1333             : 
    1334             : /************************************************************************/
    1335             : /*                          ResolveMDArray()                            */
    1336             : /************************************************************************/
    1337             : 
    1338             : /** Locate an array in a group and its subgroups by name.
    1339             :  *
    1340             :  * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
    1341             :  * used
    1342             :  * Otherwise the search will start from the group identified by osStartingPath,
    1343             :  * and an array whose name is osName will be looked for in this group (if
    1344             :  * osStartingPath is empty or "/", then the current group is used). If there
    1345             :  * is no match, then a recursive descendent search will be made in its
    1346             :  * subgroups. If there is no match in the subgroups, then the parent (if
    1347             :  * existing) of the group pointed by osStartingPath will be used as the new
    1348             :  * starting point for the search.
    1349             :  *
    1350             :  * @param osName name, qualified or not
    1351             :  * @param osStartingPath fully qualified name of the (sub-)group from which
    1352             :  *                       the search should be started. If this is a non-empty
    1353             :  *                       string, the group on which this method is called should
    1354             :  *                       nominally be the root group (otherwise the path will
    1355             :  *                       be interpreted as from the current group)
    1356             :  * @param papszOptions options to pass to OpenMDArray()
    1357             :  * @since GDAL 3.2
    1358             :  */
    1359             : std::shared_ptr<GDALMDArray>
    1360          19 : GDALGroup::ResolveMDArray(const std::string &osName,
    1361             :                           const std::string &osStartingPath,
    1362             :                           CSLConstList papszOptions) const
    1363             : {
    1364          19 :     if (!osName.empty() && osName[0] == '/')
    1365             :     {
    1366           1 :         auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
    1367           1 :         if (poArray)
    1368           1 :             return poArray;
    1369             :     }
    1370          36 :     std::string osPath(osStartingPath);
    1371          36 :     std::set<std::string> oSetAlreadyVisited;
    1372             : 
    1373             :     while (true)
    1374             :     {
    1375           0 :         std::shared_ptr<GDALGroup> curGroupHolder;
    1376           0 :         std::shared_ptr<GDALGroup> poGroup;
    1377             : 
    1378          22 :         std::queue<std::shared_ptr<GDALGroup>> oQueue;
    1379          22 :         bool goOn = false;
    1380          22 :         if (osPath.empty() || osPath == "/")
    1381             :         {
    1382          11 :             goOn = true;
    1383             :         }
    1384             :         else
    1385             :         {
    1386          22 :             std::string osLastPart;
    1387             :             const GDALGroup *poGroupPtr =
    1388          11 :                 GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
    1389          11 :             if (poGroupPtr)
    1390          11 :                 poGroup = poGroupPtr->OpenGroup(osLastPart);
    1391          22 :             if (poGroup &&
    1392          22 :                 !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
    1393             :             {
    1394          11 :                 oQueue.push(poGroup);
    1395          11 :                 goOn = true;
    1396             :             }
    1397             :         }
    1398             : 
    1399          22 :         if (goOn)
    1400             :         {
    1401          17 :             do
    1402             :             {
    1403             :                 const GDALGroup *groupPtr;
    1404          39 :                 if (!oQueue.empty())
    1405             :                 {
    1406          28 :                     poGroup = oQueue.front();
    1407          28 :                     oQueue.pop();
    1408          28 :                     groupPtr = poGroup.get();
    1409             :                 }
    1410             :                 else
    1411             :                 {
    1412          11 :                     groupPtr = this;
    1413             :                 }
    1414             : 
    1415          39 :                 auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
    1416          39 :                 if (poArray)
    1417          16 :                     return poArray;
    1418             : 
    1419          46 :                 const auto aosGroupNames = groupPtr->GetGroupNames();
    1420          47 :                 for (const auto &osGroupName : aosGroupNames)
    1421             :                 {
    1422          48 :                     auto poSubGroup = groupPtr->OpenGroup(osGroupName);
    1423          48 :                     if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
    1424          48 :                                                      poSubGroup->GetFullName()))
    1425             :                     {
    1426          24 :                         oQueue.push(poSubGroup);
    1427          24 :                         oSetAlreadyVisited.insert(poSubGroup->GetFullName());
    1428             :                     }
    1429             :                 }
    1430          23 :             } while (!oQueue.empty());
    1431             :         }
    1432             : 
    1433           6 :         if (osPath.empty() || osPath == "/")
    1434           2 :             break;
    1435             : 
    1436           4 :         const auto nPos = osPath.rfind('/');
    1437           4 :         if (nPos == 0)
    1438           1 :             osPath = "/";
    1439             :         else
    1440             :         {
    1441           3 :             if (nPos == std::string::npos)
    1442           0 :                 break;
    1443           3 :             osPath.resize(nPos);
    1444             :         }
    1445           4 :     }
    1446           2 :     return nullptr;
    1447             : }
    1448             : 
    1449             : /************************************************************************/
    1450             : /*                       OpenGroupFromFullname()                        */
    1451             : /************************************************************************/
    1452             : 
    1453             : /** Get a group from its fully qualified name.
    1454             :  * @since GDAL 3.2
    1455             :  */
    1456             : std::shared_ptr<GDALGroup>
    1457         564 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
    1458             :                                  CSLConstList papszOptions) const
    1459             : {
    1460        1128 :     std::string osName;
    1461         564 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1462         564 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1463         564 :     if (poGroup == nullptr)
    1464           4 :         return nullptr;
    1465         560 :     return poGroup->OpenGroup(osName, papszOptions);
    1466             : }
    1467             : 
    1468             : /************************************************************************/
    1469             : /*                      OpenDimensionFromFullname()                     */
    1470             : /************************************************************************/
    1471             : 
    1472             : /** Get a dimension from its fully qualified name */
    1473             : std::shared_ptr<GDALDimension>
    1474         201 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
    1475             : {
    1476         402 :     std::string osName;
    1477         201 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1478         201 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1479         201 :     if (poGroup == nullptr)
    1480           2 :         return nullptr;
    1481         398 :     auto dims(poGroup->GetDimensions());
    1482         342 :     for (auto &dim : dims)
    1483             :     {
    1484         290 :         if (dim->GetName() == osName)
    1485         147 :             return dim;
    1486             :     }
    1487          52 :     return nullptr;
    1488             : }
    1489             : 
    1490             : /************************************************************************/
    1491             : /*                           ClearStatistics()                          */
    1492             : /************************************************************************/
    1493             : 
    1494             : /**
    1495             :  * \brief Clear statistics.
    1496             :  *
    1497             :  * @since GDAL 3.4
    1498             :  */
    1499           0 : void GDALGroup::ClearStatistics()
    1500             : {
    1501           0 :     auto groupNames = GetGroupNames();
    1502           0 :     for (const auto &name : groupNames)
    1503             :     {
    1504           0 :         auto subGroup = OpenGroup(name);
    1505           0 :         if (subGroup)
    1506             :         {
    1507           0 :             subGroup->ClearStatistics();
    1508             :         }
    1509             :     }
    1510             : 
    1511           0 :     auto arrayNames = GetMDArrayNames();
    1512           0 :     for (const auto &name : arrayNames)
    1513             :     {
    1514           0 :         auto array = OpenMDArray(name);
    1515           0 :         if (array)
    1516             :         {
    1517           0 :             array->ClearStatistics();
    1518             :         }
    1519             :     }
    1520           0 : }
    1521             : 
    1522             : /************************************************************************/
    1523             : /*                            Rename()                                  */
    1524             : /************************************************************************/
    1525             : 
    1526             : /** Rename the group.
    1527             :  *
    1528             :  * This is not implemented by all drivers.
    1529             :  *
    1530             :  * Drivers known to implement it: MEM, netCDF, ZARR.
    1531             :  *
    1532             :  * This is the same as the C function GDALGroupRename().
    1533             :  *
    1534             :  * @param osNewName New name.
    1535             :  *
    1536             :  * @return true in case of success
    1537             :  * @since GDAL 3.8
    1538             :  */
    1539           0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
    1540             : {
    1541           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
    1542           0 :     return false;
    1543             : }
    1544             : 
    1545             : /************************************************************************/
    1546             : /*                         BaseRename()                                 */
    1547             : /************************************************************************/
    1548             : 
    1549             : //! @cond Doxygen_Suppress
    1550           8 : void GDALGroup::BaseRename(const std::string &osNewName)
    1551             : {
    1552           8 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
    1553           8 :     m_osFullName += osNewName;
    1554           8 :     m_osName = osNewName;
    1555             : 
    1556           8 :     NotifyChildrenOfRenaming();
    1557           8 : }
    1558             : 
    1559             : //! @endcond
    1560             : 
    1561             : /************************************************************************/
    1562             : /*                        ParentRenamed()                               */
    1563             : /************************************************************************/
    1564             : 
    1565             : //! @cond Doxygen_Suppress
    1566           7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
    1567             : {
    1568           7 :     m_osFullName = osNewParentFullName;
    1569           7 :     m_osFullName += "/";
    1570           7 :     m_osFullName += m_osName;
    1571             : 
    1572           7 :     NotifyChildrenOfRenaming();
    1573           7 : }
    1574             : 
    1575             : //! @endcond
    1576             : 
    1577             : /************************************************************************/
    1578             : /*                             Deleted()                                */
    1579             : /************************************************************************/
    1580             : 
    1581             : //! @cond Doxygen_Suppress
    1582          22 : void GDALGroup::Deleted()
    1583             : {
    1584          22 :     m_bValid = false;
    1585             : 
    1586          22 :     NotifyChildrenOfDeletion();
    1587          22 : }
    1588             : 
    1589             : //! @endcond
    1590             : 
    1591             : /************************************************************************/
    1592             : /*                        ParentDeleted()                               */
    1593             : /************************************************************************/
    1594             : 
    1595             : //! @cond Doxygen_Suppress
    1596           3 : void GDALGroup::ParentDeleted()
    1597             : {
    1598           3 :     Deleted();
    1599           3 : }
    1600             : 
    1601             : //! @endcond
    1602             : 
    1603             : /************************************************************************/
    1604             : /*                     CheckValidAndErrorOutIfNot()                     */
    1605             : /************************************************************************/
    1606             : 
    1607             : //! @cond Doxygen_Suppress
    1608       17188 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
    1609             : {
    1610       17188 :     if (!m_bValid)
    1611             :     {
    1612          14 :         CPLError(CE_Failure, CPLE_AppDefined,
    1613             :                  "This object has been deleted. No action on it is possible");
    1614             :     }
    1615       17188 :     return m_bValid;
    1616             : }
    1617             : 
    1618             : //! @endcond
    1619             : 
    1620             : /************************************************************************/
    1621             : /*                       ~GDALAbstractMDArray()                         */
    1622             : /************************************************************************/
    1623             : 
    1624             : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
    1625             : 
    1626             : /************************************************************************/
    1627             : /*                        GDALAbstractMDArray()                         */
    1628             : /************************************************************************/
    1629             : 
    1630             : //! @cond Doxygen_Suppress
    1631       35820 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
    1632       35820 :                                          const std::string &osName)
    1633             :     : m_osName(osName),
    1634             :       m_osFullName(
    1635       35820 :           !osParentName.empty()
    1636       69718 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
    1637      141358 :               : osName)
    1638             : {
    1639       35820 : }
    1640             : 
    1641             : //! @endcond
    1642             : 
    1643             : /************************************************************************/
    1644             : /*                           GetDimensions()                            */
    1645             : /************************************************************************/
    1646             : 
    1647             : /** \fn GDALAbstractMDArray::GetDimensions() const
    1648             :  * \brief Return the dimensions of an attribute/array.
    1649             :  *
    1650             :  * This is the same as the C functions GDALMDArrayGetDimensions() and
    1651             :  * similar to GDALAttributeGetDimensionsSize().
    1652             :  */
    1653             : 
    1654             : /************************************************************************/
    1655             : /*                           GetDataType()                              */
    1656             : /************************************************************************/
    1657             : 
    1658             : /** \fn GDALAbstractMDArray::GetDataType() const
    1659             :  * \brief Return the data type of an attribute/array.
    1660             :  *
    1661             :  * This is the same as the C functions GDALMDArrayGetDataType() and
    1662             :  * GDALAttributeGetDataType()
    1663             :  */
    1664             : 
    1665             : /************************************************************************/
    1666             : /*                        GetDimensionCount()                           */
    1667             : /************************************************************************/
    1668             : 
    1669             : /** Return the number of dimensions.
    1670             :  *
    1671             :  * Default implementation is GetDimensions().size(), and may be overridden by
    1672             :  * drivers if they have a faster / less expensive implementations.
    1673             :  *
    1674             :  * This is the same as the C function GDALMDArrayGetDimensionCount() or
    1675             :  * GDALAttributeGetDimensionCount().
    1676             :  *
    1677             :  */
    1678       27206 : size_t GDALAbstractMDArray::GetDimensionCount() const
    1679             : {
    1680       27206 :     return GetDimensions().size();
    1681             : }
    1682             : 
    1683             : /************************************************************************/
    1684             : /*                            Rename()                                  */
    1685             : /************************************************************************/
    1686             : 
    1687             : /** Rename the attribute/array.
    1688             :  *
    1689             :  * This is not implemented by all drivers.
    1690             :  *
    1691             :  * Drivers known to implement it: MEM, netCDF, Zarr.
    1692             :  *
    1693             :  * This is the same as the C functions GDALMDArrayRename() or
    1694             :  * GDALAttributeRename().
    1695             :  *
    1696             :  * @param osNewName New name.
    1697             :  *
    1698             :  * @return true in case of success
    1699             :  * @since GDAL 3.8
    1700             :  */
    1701           0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
    1702             : {
    1703           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
    1704           0 :     return false;
    1705             : }
    1706             : 
    1707             : /************************************************************************/
    1708             : /*                             CopyValue()                              */
    1709             : /************************************************************************/
    1710             : 
    1711             : /** Convert a value from a source type to a destination type.
    1712             :  *
    1713             :  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
    1714             :  * that must be freed with CPLFree().
    1715             :  */
    1716       84970 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
    1717             :                                      const GDALExtendedDataType &srcType,
    1718             :                                      void *pDst,
    1719             :                                      const GDALExtendedDataType &dstType)
    1720             : {
    1721      164630 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1722       79660 :         dstType.GetClass() == GEDTC_NUMERIC)
    1723             :     {
    1724       78652 :         GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
    1725             :                         dstType.GetNumericDataType(), 0, 1);
    1726       78652 :         return true;
    1727             :     }
    1728       11406 :     if (srcType.GetClass() == GEDTC_STRING &&
    1729        5088 :         dstType.GetClass() == GEDTC_STRING)
    1730             :     {
    1731             :         const char *srcStrPtr;
    1732        3840 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
    1733        3840 :         char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
    1734        3840 :         *reinterpret_cast<void **>(pDst) = pszDup;
    1735        3840 :         return true;
    1736             :     }
    1737        3486 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1738        1008 :         dstType.GetClass() == GEDTC_STRING)
    1739             :     {
    1740        1008 :         const char *str = nullptr;
    1741        1008 :         switch (srcType.GetNumericDataType())
    1742             :         {
    1743           0 :             case GDT_Unknown:
    1744           0 :                 break;
    1745           0 :             case GDT_Byte:
    1746           0 :                 str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
    1747           0 :                 break;
    1748           3 :             case GDT_Int8:
    1749           3 :                 str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
    1750           3 :                 break;
    1751          66 :             case GDT_UInt16:
    1752          66 :                 str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
    1753          66 :                 break;
    1754           0 :             case GDT_Int16:
    1755           0 :                 str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
    1756           0 :                 break;
    1757          26 :             case GDT_UInt32:
    1758          26 :                 str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
    1759          26 :                 break;
    1760          69 :             case GDT_Int32:
    1761          69 :                 str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
    1762          69 :                 break;
    1763           0 :             case GDT_UInt64:
    1764             :                 str =
    1765           0 :                     CPLSPrintf(CPL_FRMT_GUIB,
    1766             :                                static_cast<GUIntBig>(
    1767             :                                    *static_cast<const std::uint64_t *>(pSrc)));
    1768           0 :                 break;
    1769          30 :             case GDT_Int64:
    1770          30 :                 str = CPLSPrintf(CPL_FRMT_GIB,
    1771             :                                  static_cast<GIntBig>(
    1772             :                                      *static_cast<const std::int64_t *>(pSrc)));
    1773          30 :                 break;
    1774           0 :             case GDT_Float16:
    1775           0 :                 str = CPLSPrintf("%.5g",
    1776             :                                  double(*static_cast<const GFloat16 *>(pSrc)));
    1777           0 :                 break;
    1778          60 :             case GDT_Float32:
    1779         120 :                 str = CPLSPrintf(
    1780             :                     "%.9g",
    1781          60 :                     static_cast<double>(*static_cast<const float *>(pSrc)));
    1782          60 :                 break;
    1783         752 :             case GDT_Float64:
    1784         752 :                 str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
    1785         752 :                 break;
    1786           2 :             case GDT_CInt16:
    1787             :             {
    1788           2 :                 const GInt16 *src = static_cast<const GInt16 *>(pSrc);
    1789           2 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
    1790           2 :                 break;
    1791             :             }
    1792           0 :             case GDT_CInt32:
    1793             :             {
    1794           0 :                 const GInt32 *src = static_cast<const GInt32 *>(pSrc);
    1795           0 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
    1796           0 :                 break;
    1797             :             }
    1798           0 :             case GDT_CFloat16:
    1799             :             {
    1800           0 :                 const GFloat16 *src = static_cast<const GFloat16 *>(pSrc);
    1801           0 :                 str = CPLSPrintf("%.5g+%.5gj", double(src[0]), double(src[1]));
    1802           0 :                 break;
    1803             :             }
    1804           0 :             case GDT_CFloat32:
    1805             :             {
    1806           0 :                 const float *src = static_cast<const float *>(pSrc);
    1807           0 :                 str = CPLSPrintf("%.9g+%.9gj", double(src[0]), double(src[1]));
    1808           0 :                 break;
    1809             :             }
    1810           0 :             case GDT_CFloat64:
    1811             :             {
    1812           0 :                 const double *src = static_cast<const double *>(pSrc);
    1813           0 :                 str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
    1814           0 :                 break;
    1815             :             }
    1816           0 :             case GDT_TypeCount:
    1817           0 :                 CPLAssert(false);
    1818             :                 break;
    1819             :         }
    1820        1008 :         char *pszDup = str ? CPLStrdup(str) : nullptr;
    1821        1008 :         *reinterpret_cast<void **>(pDst) = pszDup;
    1822        1008 :         return true;
    1823             :     }
    1824        2718 :     if (srcType.GetClass() == GEDTC_STRING &&
    1825        1248 :         dstType.GetClass() == GEDTC_NUMERIC)
    1826             :     {
    1827             :         const char *srcStrPtr;
    1828        1248 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
    1829        1248 :         if (dstType.GetNumericDataType() == GDT_Int64)
    1830             :         {
    1831           2 :             *(static_cast<int64_t *>(pDst)) =
    1832           2 :                 srcStrPtr == nullptr ? 0
    1833           1 :                                      : static_cast<int64_t>(atoll(srcStrPtr));
    1834             :         }
    1835        1246 :         else if (dstType.GetNumericDataType() == GDT_UInt64)
    1836             :         {
    1837           2 :             *(static_cast<uint64_t *>(pDst)) =
    1838           2 :                 srcStrPtr == nullptr
    1839           2 :                     ? 0
    1840           1 :                     : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
    1841             :         }
    1842             :         else
    1843             :         {
    1844        1244 :             const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
    1845        1244 :             GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
    1846             :                             dstType.GetNumericDataType(), 0, 1);
    1847             :         }
    1848        1248 :         return true;
    1849             :     }
    1850         444 :     if (srcType.GetClass() == GEDTC_COMPOUND &&
    1851         222 :         dstType.GetClass() == GEDTC_COMPOUND)
    1852             :     {
    1853         222 :         const auto &srcComponents = srcType.GetComponents();
    1854         222 :         const auto &dstComponents = dstType.GetComponents();
    1855         222 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
    1856         222 :         GByte *pabyDst = static_cast<GByte *>(pDst);
    1857             : 
    1858             :         std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
    1859         444 :             srcComponentMap;
    1860        1078 :         for (const auto &srcComp : srcComponents)
    1861             :         {
    1862         856 :             srcComponentMap[srcComp->GetName()] = &srcComp;
    1863             :         }
    1864         598 :         for (const auto &dstComp : dstComponents)
    1865             :         {
    1866         376 :             auto oIter = srcComponentMap.find(dstComp->GetName());
    1867         376 :             if (oIter == srcComponentMap.end())
    1868           0 :                 return false;
    1869         376 :             const auto &srcComp = *(oIter->second);
    1870        1128 :             if (!GDALExtendedDataType::CopyValue(
    1871         376 :                     pabySrc + srcComp->GetOffset(), srcComp->GetType(),
    1872         376 :                     pabyDst + dstComp->GetOffset(), dstComp->GetType()))
    1873             :             {
    1874           0 :                 return false;
    1875             :             }
    1876             :         }
    1877         222 :         return true;
    1878             :     }
    1879             : 
    1880           0 :     return false;
    1881             : }
    1882             : 
    1883             : /************************************************************************/
    1884             : /*                             CopyValues()                             */
    1885             : /************************************************************************/
    1886             : 
    1887             : /** Convert severals value from a source type to a destination type.
    1888             :  *
    1889             :  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
    1890             :  * that must be freed with CPLFree().
    1891             :  */
    1892         370 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
    1893             :                                       const GDALExtendedDataType &srcType,
    1894             :                                       GPtrDiff_t nSrcStrideInElts, void *pDst,
    1895             :                                       const GDALExtendedDataType &dstType,
    1896             :                                       GPtrDiff_t nDstStrideInElts,
    1897             :                                       size_t nValues)
    1898             : {
    1899             :     const auto nSrcStrideInBytes =
    1900         370 :         nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
    1901             :     const auto nDstStrideInBytes =
    1902         370 :         nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
    1903         636 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1904         266 :         dstType.GetClass() == GEDTC_NUMERIC &&
    1905         266 :         nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
    1906         266 :         nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
    1907         902 :         nDstStrideInBytes >= std::numeric_limits<int>::min() &&
    1908         266 :         nDstStrideInBytes <= std::numeric_limits<int>::max())
    1909             :     {
    1910         266 :         GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
    1911             :                         static_cast<int>(nSrcStrideInBytes), pDst,
    1912             :                         dstType.GetNumericDataType(),
    1913             :                         static_cast<int>(nDstStrideInBytes), nValues);
    1914             :     }
    1915             :     else
    1916             :     {
    1917         104 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
    1918         104 :         GByte *pabyDst = static_cast<GByte *>(pDst);
    1919         208 :         for (size_t i = 0; i < nValues; ++i)
    1920             :         {
    1921         104 :             if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
    1922           0 :                 return false;
    1923         104 :             pabySrc += nSrcStrideInBytes;
    1924         104 :             pabyDst += nDstStrideInBytes;
    1925             :         }
    1926             :     }
    1927         370 :     return true;
    1928             : }
    1929             : 
    1930             : /************************************************************************/
    1931             : /*                       CheckReadWriteParams()                         */
    1932             : /************************************************************************/
    1933             : //! @cond Doxygen_Suppress
    1934       10185 : bool GDALAbstractMDArray::CheckReadWriteParams(
    1935             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
    1936             :     const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
    1937             :     const void *buffer, const void *buffer_alloc_start,
    1938             :     size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
    1939             :     std::vector<GPtrDiff_t> &tmp_bufferStride) const
    1940             : {
    1941           0 :     const auto lamda_error = []()
    1942             :     {
    1943           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1944             :                  "Not all elements pointed by buffer will fit in "
    1945             :                  "[buffer_alloc_start, "
    1946             :                  "buffer_alloc_start + buffer_alloc_size]");
    1947           0 :     };
    1948             : 
    1949       10185 :     const auto &dims = GetDimensions();
    1950       10185 :     if (dims.empty())
    1951             :     {
    1952        4430 :         if (buffer_alloc_start)
    1953             :         {
    1954        4032 :             const size_t elementSize = bufferDataType.GetSize();
    1955        4032 :             const GByte *paby_buffer = static_cast<const GByte *>(buffer);
    1956        4032 :             const GByte *paby_buffer_alloc_start =
    1957             :                 static_cast<const GByte *>(buffer_alloc_start);
    1958        4032 :             const GByte *paby_buffer_alloc_end =
    1959             :                 paby_buffer_alloc_start + buffer_alloc_size;
    1960             : 
    1961        4032 :             if (paby_buffer < paby_buffer_alloc_start ||
    1962        4032 :                 paby_buffer + elementSize > paby_buffer_alloc_end)
    1963             :             {
    1964           0 :                 lamda_error();
    1965           0 :                 return false;
    1966             :             }
    1967             :         }
    1968        4430 :         return true;
    1969             :     }
    1970             : 
    1971        5755 :     if (arrayStep == nullptr)
    1972             :     {
    1973        1670 :         tmp_arrayStep.resize(dims.size(), 1);
    1974        1670 :         arrayStep = tmp_arrayStep.data();
    1975             :     }
    1976       15806 :     for (size_t i = 0; i < dims.size(); i++)
    1977             :     {
    1978       10051 :         assert(count);
    1979       10051 :         if (count[i] == 0)
    1980             :         {
    1981           0 :             CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
    1982             :                      static_cast<unsigned>(i));
    1983           0 :             return false;
    1984             :         }
    1985             :     }
    1986        5755 :     bool bufferStride_all_positive = true;
    1987        5755 :     if (bufferStride == nullptr)
    1988             :     {
    1989        1350 :         GPtrDiff_t stride = 1;
    1990        1350 :         assert(dims.empty() || count != nullptr);
    1991             :         // To compute strides we must proceed from the fastest varying dimension
    1992             :         // (the last one), and then reverse the result
    1993        3008 :         for (size_t i = dims.size(); i != 0;)
    1994             :         {
    1995        1658 :             --i;
    1996        1658 :             tmp_bufferStride.push_back(stride);
    1997        1658 :             GUInt64 newStride = 0;
    1998             :             bool bOK;
    1999             :             try
    2000             :             {
    2001        1658 :                 newStride = (CPLSM(static_cast<uint64_t>(stride)) *
    2002        3316 :                              CPLSM(static_cast<uint64_t>(count[i])))
    2003        1658 :                                 .v();
    2004        1658 :                 bOK = static_cast<size_t>(newStride) == newStride &&
    2005        1658 :                       newStride < std::numeric_limits<size_t>::max() / 2;
    2006             :             }
    2007           0 :             catch (...)
    2008             :             {
    2009           0 :                 bOK = false;
    2010             :             }
    2011        1658 :             if (!bOK)
    2012             :             {
    2013           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
    2014           0 :                 return false;
    2015             :             }
    2016        1658 :             stride = static_cast<GPtrDiff_t>(newStride);
    2017             :         }
    2018        1350 :         std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
    2019        1350 :         bufferStride = tmp_bufferStride.data();
    2020             :     }
    2021             :     else
    2022             :     {
    2023       12796 :         for (size_t i = 0; i < dims.size(); i++)
    2024             :         {
    2025        8392 :             if (bufferStride[i] < 0)
    2026             :             {
    2027           1 :                 bufferStride_all_positive = false;
    2028           1 :                 break;
    2029             :             }
    2030             :         }
    2031             :     }
    2032       15777 :     for (size_t i = 0; i < dims.size(); i++)
    2033             :     {
    2034       10032 :         assert(arrayStartIdx);
    2035       10032 :         assert(count);
    2036       10032 :         if (arrayStartIdx[i] >= dims[i]->GetSize())
    2037             :         {
    2038           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    2039             :                      "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
    2040             :                      static_cast<unsigned>(i),
    2041           2 :                      static_cast<GUInt64>(arrayStartIdx[i]),
    2042           2 :                      static_cast<GUInt64>(dims[i]->GetSize()));
    2043           2 :             return false;
    2044             :         }
    2045             :         bool bOverflow;
    2046       10030 :         if (arrayStep[i] >= 0)
    2047             :         {
    2048             :             try
    2049             :             {
    2050        9375 :                 bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
    2051        9377 :                              CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    2052       37503 :                                  CPLSM(static_cast<uint64_t>(arrayStep[i])))
    2053        9375 :                                 .v() >= dims[i]->GetSize();
    2054             :             }
    2055           1 :             catch (...)
    2056             :             {
    2057           1 :                 bOverflow = true;
    2058             :             }
    2059        9376 :             if (bOverflow)
    2060             :             {
    2061           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2062             :                          "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
    2063             :                          ">= " CPL_FRMT_GUIB,
    2064             :                          static_cast<unsigned>(i), static_cast<unsigned>(i),
    2065             :                          static_cast<unsigned>(i),
    2066           5 :                          static_cast<GUInt64>(dims[i]->GetSize()));
    2067           5 :                 return false;
    2068             :             }
    2069             :         }
    2070             :         else
    2071             :         {
    2072             :             try
    2073             :             {
    2074         654 :                 bOverflow =
    2075         654 :                     arrayStartIdx[i] <
    2076         654 :                     (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    2077        1308 :                      CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
    2078             :                                ? (static_cast<uint64_t>(1) << 63)
    2079        1308 :                                : static_cast<uint64_t>(-arrayStep[i])))
    2080         654 :                         .v();
    2081             :             }
    2082           0 :             catch (...)
    2083             :             {
    2084           0 :                 bOverflow = true;
    2085             :             }
    2086         654 :             if (bOverflow)
    2087             :             {
    2088           3 :                 CPLError(
    2089             :                     CE_Failure, CPLE_AppDefined,
    2090             :                     "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
    2091             :                     static_cast<unsigned>(i), static_cast<unsigned>(i),
    2092             :                     static_cast<unsigned>(i));
    2093           3 :                 return false;
    2094             :             }
    2095             :         }
    2096             :     }
    2097             : 
    2098        5745 :     if (buffer_alloc_start)
    2099             :     {
    2100        2798 :         const size_t elementSize = bufferDataType.GetSize();
    2101        2798 :         const GByte *paby_buffer = static_cast<const GByte *>(buffer);
    2102        2798 :         const GByte *paby_buffer_alloc_start =
    2103             :             static_cast<const GByte *>(buffer_alloc_start);
    2104        2798 :         const GByte *paby_buffer_alloc_end =
    2105             :             paby_buffer_alloc_start + buffer_alloc_size;
    2106        2798 :         if (bufferStride_all_positive)
    2107             :         {
    2108        2798 :             if (paby_buffer < paby_buffer_alloc_start)
    2109             :             {
    2110           0 :                 lamda_error();
    2111           0 :                 return false;
    2112             :             }
    2113        2798 :             GUInt64 nOffset = elementSize;
    2114        7972 :             for (size_t i = 0; i < dims.size(); i++)
    2115             :             {
    2116             :                 try
    2117             :                 {
    2118        5174 :                     nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
    2119        5174 :                                CPLSM(static_cast<uint64_t>(bufferStride[i])) *
    2120       10348 :                                    CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    2121       20696 :                                    CPLSM(static_cast<uint64_t>(elementSize)))
    2122        5174 :                                   .v();
    2123             :                 }
    2124           0 :                 catch (...)
    2125             :                 {
    2126           0 :                     lamda_error();
    2127           0 :                     return false;
    2128             :                 }
    2129             :             }
    2130             : #if SIZEOF_VOIDP == 4
    2131             :             if (static_cast<size_t>(nOffset) != nOffset)
    2132             :             {
    2133             :                 lamda_error();
    2134             :                 return false;
    2135             :             }
    2136             : #endif
    2137        2798 :             if (paby_buffer + nOffset > paby_buffer_alloc_end)
    2138             :             {
    2139           0 :                 lamda_error();
    2140           0 :                 return false;
    2141             :             }
    2142             :         }
    2143           0 :         else if (dims.size() < 31)
    2144             :         {
    2145             :             // Check all corners of the hypercube
    2146           0 :             const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
    2147           0 :             for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
    2148             :             {
    2149           0 :                 const GByte *paby = paby_buffer;
    2150           0 :                 for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
    2151             :                      i++)
    2152             :                 {
    2153           0 :                     if (iCornerCode & (1U << i))
    2154             :                     {
    2155             :                         // We should check for integer overflows
    2156           0 :                         paby += bufferStride[i] * (count[i] - 1) * elementSize;
    2157             :                     }
    2158             :                 }
    2159           0 :                 if (paby < paby_buffer_alloc_start ||
    2160           0 :                     paby + elementSize > paby_buffer_alloc_end)
    2161             :                 {
    2162           0 :                     lamda_error();
    2163           0 :                     return false;
    2164             :                 }
    2165             :             }
    2166             :         }
    2167             :     }
    2168             : 
    2169        5745 :     return true;
    2170             : }
    2171             : 
    2172             : //! @endcond
    2173             : 
    2174             : /************************************************************************/
    2175             : /*                               Read()                                 */
    2176             : /************************************************************************/
    2177             : 
    2178             : /** Read part or totality of a multidimensional array or attribute.
    2179             :  *
    2180             :  * This will extract the content of a hyper-rectangle from the array into
    2181             :  * a user supplied buffer.
    2182             :  *
    2183             :  * If bufferDataType is of type string, the values written in pDstBuffer
    2184             :  * will be char* pointers and the strings should be freed with CPLFree().
    2185             :  *
    2186             :  * This is the same as the C function GDALMDArrayRead().
    2187             :  *
    2188             :  * @param arrayStartIdx Values representing the starting index to read
    2189             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2190             :  *                      Array of GetDimensionCount() values. Must not be
    2191             :  *                      nullptr, unless for a zero-dimensional array.
    2192             :  *
    2193             :  * @param count         Values representing the number of values to extract in
    2194             :  *                      each dimension.
    2195             :  *                      Array of GetDimensionCount() values. Must not be
    2196             :  *                      nullptr, unless for a zero-dimensional array.
    2197             :  *
    2198             :  * @param arrayStep     Spacing between values to extract in each dimension.
    2199             :  *                      The spacing is in number of array elements, not bytes.
    2200             :  *                      If provided, must contain GetDimensionCount() values.
    2201             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
    2202             :  * default to indicate consecutive elements.
    2203             :  *
    2204             :  * @param bufferStride  Spacing between values to store in pDstBuffer.
    2205             :  *                      The spacing is in number of array elements, not bytes.
    2206             :  *                      If provided, must contain GetDimensionCount() values.
    2207             :  *                      Negative values are possible (for example to reorder
    2208             :  *                      from bottom-to-top to top-to-bottom).
    2209             :  *                      If set to nullptr, will be set so that pDstBuffer is
    2210             :  *                      written in a compact way, with elements of the last /
    2211             :  *                      fastest varying dimension being consecutive.
    2212             :  *
    2213             :  * @param bufferDataType Data type of values in pDstBuffer.
    2214             :  *
    2215             :  * @param pDstBuffer    User buffer to store the values read. Should be big
    2216             :  *                      enough to store the number of values indicated by
    2217             :  * count[] and with the spacing of bufferStride[].
    2218             :  *
    2219             :  * @param pDstBufferAllocStart Optional pointer that can be used to validate the
    2220             :  *                             validity of pDstBuffer. pDstBufferAllocStart
    2221             :  * should be the pointer returned by the malloc() or equivalent call used to
    2222             :  * allocate the buffer. It will generally be equal to pDstBuffer (when
    2223             :  * bufferStride[] values are all positive), but not necessarily. If specified,
    2224             :  * nDstBufferAllocSize should be also set to the appropriate value. If no
    2225             :  * validation is needed, nullptr can be passed.
    2226             :  *
    2227             :  * @param nDstBufferAllocSize  Optional buffer size, that can be used to
    2228             :  * validate the validity of pDstBuffer. This is the size of the buffer starting
    2229             :  * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
    2230             :  *                             set to the appropriate value.
    2231             :  *                             If no validation is needed, 0 can be passed.
    2232             :  *
    2233             :  * @return true in case of success.
    2234             :  */
    2235        3660 : bool GDALAbstractMDArray::Read(
    2236             :     const GUInt64 *arrayStartIdx, const size_t *count,
    2237             :     const GInt64 *arrayStep,         // step in elements
    2238             :     const GPtrDiff_t *bufferStride,  // stride in elements
    2239             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    2240             :     const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
    2241             : {
    2242        3660 :     if (!GetDataType().CanConvertTo(bufferDataType))
    2243             :     {
    2244           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2245             :                  "Array data type is not convertible to buffer data type");
    2246           0 :         return false;
    2247             :     }
    2248             : 
    2249        7320 :     std::vector<GInt64> tmp_arrayStep;
    2250        7320 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    2251        3660 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    2252             :                               bufferDataType, pDstBuffer, pDstBufferAllocStart,
    2253             :                               nDstBufferAllocSize, tmp_arrayStep,
    2254             :                               tmp_bufferStride))
    2255             :     {
    2256           0 :         return false;
    2257             :     }
    2258             : 
    2259        3660 :     return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
    2260        3660 :                  pDstBuffer);
    2261             : }
    2262             : 
    2263             : /************************************************************************/
    2264             : /*                                IWrite()                              */
    2265             : /************************************************************************/
    2266             : 
    2267             : //! @cond Doxygen_Suppress
    2268           1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
    2269             :                                  const GInt64 *, const GPtrDiff_t *,
    2270             :                                  const GDALExtendedDataType &, const void *)
    2271             : {
    2272           1 :     CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
    2273           1 :     return false;
    2274             : }
    2275             : 
    2276             : //! @endcond
    2277             : 
    2278             : /************************************************************************/
    2279             : /*                               Write()                                 */
    2280             : /************************************************************************/
    2281             : 
    2282             : /** Write part or totality of a multidimensional array or attribute.
    2283             :  *
    2284             :  * This will set the content of a hyper-rectangle into the array from
    2285             :  * a user supplied buffer.
    2286             :  *
    2287             :  * If bufferDataType is of type string, the values read from pSrcBuffer
    2288             :  * will be char* pointers.
    2289             :  *
    2290             :  * This is the same as the C function GDALMDArrayWrite().
    2291             :  *
    2292             :  * @param arrayStartIdx Values representing the starting index to write
    2293             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2294             :  *                      Array of GetDimensionCount() values. Must not be
    2295             :  *                      nullptr, unless for a zero-dimensional array.
    2296             :  *
    2297             :  * @param count         Values representing the number of values to write in
    2298             :  *                      each dimension.
    2299             :  *                      Array of GetDimensionCount() values. Must not be
    2300             :  *                      nullptr, unless for a zero-dimensional array.
    2301             :  *
    2302             :  * @param arrayStep     Spacing between values to write in each dimension.
    2303             :  *                      The spacing is in number of array elements, not bytes.
    2304             :  *                      If provided, must contain GetDimensionCount() values.
    2305             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
    2306             :  * default to indicate consecutive elements.
    2307             :  *
    2308             :  * @param bufferStride  Spacing between values to read from pSrcBuffer.
    2309             :  *                      The spacing is in number of array elements, not bytes.
    2310             :  *                      If provided, must contain GetDimensionCount() values.
    2311             :  *                      Negative values are possible (for example to reorder
    2312             :  *                      from bottom-to-top to top-to-bottom).
    2313             :  *                      If set to nullptr, will be set so that pSrcBuffer is
    2314             :  *                      written in a compact way, with elements of the last /
    2315             :  *                      fastest varying dimension being consecutive.
    2316             :  *
    2317             :  * @param bufferDataType Data type of values in pSrcBuffer.
    2318             :  *
    2319             :  * @param pSrcBuffer    User buffer to read the values from. Should be big
    2320             :  *                      enough to store the number of values indicated by
    2321             :  * count[] and with the spacing of bufferStride[].
    2322             :  *
    2323             :  * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
    2324             :  *                             validity of pSrcBuffer. pSrcBufferAllocStart
    2325             :  * should be the pointer returned by the malloc() or equivalent call used to
    2326             :  * allocate the buffer. It will generally be equal to pSrcBuffer (when
    2327             :  * bufferStride[] values are all positive), but not necessarily. If specified,
    2328             :  * nSrcBufferAllocSize should be also set to the appropriate value. If no
    2329             :  * validation is needed, nullptr can be passed.
    2330             :  *
    2331             :  * @param nSrcBufferAllocSize  Optional buffer size, that can be used to
    2332             :  * validate the validity of pSrcBuffer. This is the size of the buffer starting
    2333             :  * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
    2334             :  *                             set to the appropriate value.
    2335             :  *                             If no validation is needed, 0 can be passed.
    2336             :  *
    2337             :  * @return true in case of success.
    2338             :  */
    2339        2162 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
    2340             :                                 const size_t *count, const GInt64 *arrayStep,
    2341             :                                 const GPtrDiff_t *bufferStride,
    2342             :                                 const GDALExtendedDataType &bufferDataType,
    2343             :                                 const void *pSrcBuffer,
    2344             :                                 const void *pSrcBufferAllocStart,
    2345             :                                 size_t nSrcBufferAllocSize)
    2346             : {
    2347        2162 :     if (!bufferDataType.CanConvertTo(GetDataType()))
    2348             :     {
    2349           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2350             :                  "Buffer data type is not convertible to array data type");
    2351           0 :         return false;
    2352             :     }
    2353             : 
    2354        4324 :     std::vector<GInt64> tmp_arrayStep;
    2355        4324 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    2356        2162 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    2357             :                               bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
    2358             :                               nSrcBufferAllocSize, tmp_arrayStep,
    2359             :                               tmp_bufferStride))
    2360             :     {
    2361           0 :         return false;
    2362             :     }
    2363             : 
    2364        2162 :     return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
    2365        2162 :                   pSrcBuffer);
    2366             : }
    2367             : 
    2368             : /************************************************************************/
    2369             : /*                          GetTotalElementsCount()                     */
    2370             : /************************************************************************/
    2371             : 
    2372             : /** Return the total number of values in the array.
    2373             :  *
    2374             :  * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
    2375             :  * and GDALAttributeGetTotalElementsCount().
    2376             :  *
    2377             :  */
    2378        1408 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
    2379             : {
    2380        1408 :     const auto &dims = GetDimensions();
    2381        1408 :     if (dims.empty())
    2382         752 :         return 1;
    2383         656 :     GUInt64 nElts = 1;
    2384        1448 :     for (const auto &dim : dims)
    2385             :     {
    2386             :         try
    2387             :         {
    2388         792 :             nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
    2389        2376 :                      CPLSM(static_cast<uint64_t>(dim->GetSize())))
    2390         792 :                         .v();
    2391             :         }
    2392           0 :         catch (...)
    2393             :         {
    2394           0 :             return 0;
    2395             :         }
    2396             :     }
    2397         656 :     return nElts;
    2398             : }
    2399             : 
    2400             : /************************************************************************/
    2401             : /*                           GetBlockSize()                             */
    2402             : /************************************************************************/
    2403             : 
    2404             : /** Return the "natural" block size of the array along all dimensions.
    2405             :  *
    2406             :  * Some drivers might organize the array in tiles/blocks and reading/writing
    2407             :  * aligned on those tile/block boundaries will be more efficient.
    2408             :  *
    2409             :  * The returned number of elements in the vector is the same as
    2410             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
    2411             :  * the natural block size along the considered dimension.
    2412             :  * "Flat" arrays will typically return a vector of values set to 0.
    2413             :  *
    2414             :  * The default implementation will return a vector of values set to 0.
    2415             :  *
    2416             :  * This method is used by GetProcessingChunkSize().
    2417             :  *
    2418             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
    2419             :  * theoretical case of a 32-bit platform, this might exceed its size_t
    2420             :  * allocation capabilities.
    2421             :  *
    2422             :  * This is the same as the C function GDALMDArrayGetBlockSize().
    2423             :  *
    2424             :  * @return the block size, in number of elements along each dimension.
    2425             :  */
    2426         297 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
    2427             : {
    2428         297 :     return std::vector<GUInt64>(GetDimensionCount());
    2429             : }
    2430             : 
    2431             : /************************************************************************/
    2432             : /*                       GetProcessingChunkSize()                       */
    2433             : /************************************************************************/
    2434             : 
    2435             : /** \brief Return an optimal chunk size for read/write operations, given the
    2436             :  * natural block size and memory constraints specified.
    2437             :  *
    2438             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
    2439             :  * multiple of those returned by GetBlockSize() (unless the block define by
    2440             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
    2441             :  * returned by this method).
    2442             :  *
    2443             :  * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
    2444             :  *
    2445             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
    2446             :  * chunk.
    2447             :  *
    2448             :  * @return the chunk size, in number of elements along each dimension.
    2449             :  */
    2450             : std::vector<size_t>
    2451          90 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
    2452             : {
    2453          90 :     const auto &dims = GetDimensions();
    2454          90 :     const auto &nDTSize = GetDataType().GetSize();
    2455          90 :     std::vector<size_t> anChunkSize;
    2456         180 :     auto blockSize = GetBlockSize();
    2457          90 :     CPLAssert(blockSize.size() == dims.size());
    2458          90 :     size_t nChunkSize = nDTSize;
    2459          90 :     bool bOverflow = false;
    2460          90 :     constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
    2461             :     // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
    2462             :     // [1, min(sizet_max, dim_size[i])]
    2463             :     // Also make sure that the product of all anChunkSize[i]) fits on size_t
    2464         246 :     for (size_t i = 0; i < dims.size(); i++)
    2465             :     {
    2466             :         const auto sizeDimI =
    2467         312 :             std::max(static_cast<size_t>(1),
    2468         312 :                      static_cast<size_t>(
    2469         312 :                          std::min(static_cast<GUInt64>(kSIZE_T_MAX),
    2470         156 :                                   std::min(blockSize[i], dims[i]->GetSize()))));
    2471         156 :         anChunkSize.push_back(sizeDimI);
    2472         156 :         if (nChunkSize > kSIZE_T_MAX / sizeDimI)
    2473             :         {
    2474           4 :             bOverflow = true;
    2475             :         }
    2476             :         else
    2477             :         {
    2478         152 :             nChunkSize *= sizeDimI;
    2479             :         }
    2480             :     }
    2481          90 :     if (nChunkSize == 0)
    2482           0 :         return anChunkSize;
    2483             : 
    2484             :     // If the product of all anChunkSize[i] does not fit on size_t, then
    2485             :     // set lowest anChunkSize[i] to 1.
    2486          90 :     if (bOverflow)
    2487             :     {
    2488           2 :         nChunkSize = nDTSize;
    2489           2 :         bOverflow = false;
    2490           8 :         for (size_t i = dims.size(); i > 0;)
    2491             :         {
    2492           6 :             --i;
    2493           6 :             if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
    2494             :             {
    2495           4 :                 bOverflow = true;
    2496           4 :                 anChunkSize[i] = 1;
    2497             :             }
    2498             :             else
    2499             :             {
    2500           2 :                 nChunkSize *= anChunkSize[i];
    2501             :             }
    2502             :         }
    2503             :     }
    2504             : 
    2505          90 :     nChunkSize = nDTSize;
    2506         180 :     std::vector<size_t> anAccBlockSizeFromStart;
    2507         246 :     for (size_t i = 0; i < dims.size(); i++)
    2508             :     {
    2509         156 :         nChunkSize *= anChunkSize[i];
    2510         156 :         anAccBlockSizeFromStart.push_back(nChunkSize);
    2511             :     }
    2512          90 :     if (nChunkSize <= nMaxChunkMemory / 2)
    2513             :     {
    2514          86 :         size_t nVoxelsFromEnd = 1;
    2515         234 :         for (size_t i = dims.size(); i > 0;)
    2516             :         {
    2517         148 :             --i;
    2518             :             const auto nCurBlockSize =
    2519         148 :                 anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
    2520         148 :             const auto nMul = nMaxChunkMemory / nCurBlockSize;
    2521         148 :             if (nMul >= 2)
    2522             :             {
    2523         140 :                 const auto nSizeThisDim(dims[i]->GetSize());
    2524             :                 const auto nBlocksThisDim =
    2525         140 :                     DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
    2526         140 :                 anChunkSize[i] = static_cast<size_t>(std::min(
    2527         140 :                     anChunkSize[i] *
    2528         280 :                         std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
    2529         140 :                     nSizeThisDim));
    2530             :             }
    2531         148 :             nVoxelsFromEnd *= anChunkSize[i];
    2532             :         }
    2533             :     }
    2534          90 :     return anChunkSize;
    2535             : }
    2536             : 
    2537             : /************************************************************************/
    2538             : /*                         BaseRename()                                 */
    2539             : /************************************************************************/
    2540             : 
    2541             : //! @cond Doxygen_Suppress
    2542          18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
    2543             : {
    2544          18 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
    2545          18 :     m_osFullName += osNewName;
    2546          18 :     m_osName = osNewName;
    2547             : 
    2548          18 :     NotifyChildrenOfRenaming();
    2549          18 : }
    2550             : 
    2551             : //! @endcond
    2552             : 
    2553             : //! @cond Doxygen_Suppress
    2554             : /************************************************************************/
    2555             : /*                          ParentRenamed()                             */
    2556             : /************************************************************************/
    2557             : 
    2558          50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
    2559             : {
    2560          50 :     m_osFullName = osNewParentFullName;
    2561          50 :     m_osFullName += "/";
    2562          50 :     m_osFullName += m_osName;
    2563             : 
    2564          50 :     NotifyChildrenOfRenaming();
    2565          50 : }
    2566             : 
    2567             : //! @endcond
    2568             : 
    2569             : /************************************************************************/
    2570             : /*                             Deleted()                                */
    2571             : /************************************************************************/
    2572             : 
    2573             : //! @cond Doxygen_Suppress
    2574          52 : void GDALAbstractMDArray::Deleted()
    2575             : {
    2576          52 :     m_bValid = false;
    2577             : 
    2578          52 :     NotifyChildrenOfDeletion();
    2579          52 : }
    2580             : 
    2581             : //! @endcond
    2582             : 
    2583             : /************************************************************************/
    2584             : /*                        ParentDeleted()                               */
    2585             : /************************************************************************/
    2586             : 
    2587             : //! @cond Doxygen_Suppress
    2588          28 : void GDALAbstractMDArray::ParentDeleted()
    2589             : {
    2590          28 :     Deleted();
    2591          28 : }
    2592             : 
    2593             : //! @endcond
    2594             : 
    2595             : /************************************************************************/
    2596             : /*                     CheckValidAndErrorOutIfNot()                     */
    2597             : /************************************************************************/
    2598             : 
    2599             : //! @cond Doxygen_Suppress
    2600        6550 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
    2601             : {
    2602        6550 :     if (!m_bValid)
    2603             :     {
    2604          26 :         CPLError(CE_Failure, CPLE_AppDefined,
    2605             :                  "This object has been deleted. No action on it is possible");
    2606             :     }
    2607        6550 :     return m_bValid;
    2608             : }
    2609             : 
    2610             : //! @endcond
    2611             : 
    2612             : /************************************************************************/
    2613             : /*                             SetUnit()                                */
    2614             : /************************************************************************/
    2615             : 
    2616             : /** Set the variable unit.
    2617             :  *
    2618             :  * Values should conform as much as possible with those allowed by
    2619             :  * the NetCDF CF conventions:
    2620             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
    2621             :  * but others might be returned.
    2622             :  *
    2623             :  * Few examples are "meter", "degrees", "second", ...
    2624             :  * Empty value means unknown.
    2625             :  *
    2626             :  * This is the same as the C function GDALMDArraySetUnit()
    2627             :  *
    2628             :  * @note Driver implementation: optionally implemented.
    2629             :  *
    2630             :  * @param osUnit unit name.
    2631             :  * @return true in case of success.
    2632             :  */
    2633           0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
    2634             : {
    2635           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
    2636           0 :     return false;
    2637             : }
    2638             : 
    2639             : /************************************************************************/
    2640             : /*                             GetUnit()                                */
    2641             : /************************************************************************/
    2642             : 
    2643             : /** Return the array unit.
    2644             :  *
    2645             :  * Values should conform as much as possible with those allowed by
    2646             :  * the NetCDF CF conventions:
    2647             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
    2648             :  * but others might be returned.
    2649             :  *
    2650             :  * Few examples are "meter", "degrees", "second", ...
    2651             :  * Empty value means unknown.
    2652             :  *
    2653             :  * This is the same as the C function GDALMDArrayGetUnit()
    2654             :  */
    2655           5 : const std::string &GDALMDArray::GetUnit() const
    2656             : {
    2657           5 :     static const std::string emptyString;
    2658           5 :     return emptyString;
    2659             : }
    2660             : 
    2661             : /************************************************************************/
    2662             : /*                          SetSpatialRef()                             */
    2663             : /************************************************************************/
    2664             : 
    2665             : /** Assign a spatial reference system object to the array.
    2666             :  *
    2667             :  * This is the same as the C function GDALMDArraySetSpatialRef().
    2668             :  */
    2669           0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
    2670             : {
    2671           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
    2672           0 :     return false;
    2673             : }
    2674             : 
    2675             : /************************************************************************/
    2676             : /*                          GetSpatialRef()                             */
    2677             : /************************************************************************/
    2678             : 
    2679             : /** Return the spatial reference system object associated with the array.
    2680             :  *
    2681             :  * This is the same as the C function GDALMDArrayGetSpatialRef().
    2682             :  */
    2683           4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
    2684             : {
    2685           4 :     return nullptr;
    2686             : }
    2687             : 
    2688             : /************************************************************************/
    2689             : /*                        GetRawNoDataValue()                           */
    2690             : /************************************************************************/
    2691             : 
    2692             : /** Return the nodata value as a "raw" value.
    2693             :  *
    2694             :  * The value returned might be nullptr in case of no nodata value. When
    2695             :  * a nodata value is registered, a non-nullptr will be returned whose size in
    2696             :  * bytes is GetDataType().GetSize().
    2697             :  *
    2698             :  * The returned value should not be modified or freed. It is valid until
    2699             :  * the array is destroyed, or the next call to GetRawNoDataValue() or
    2700             :  * SetRawNoDataValue(), or any similar methods.
    2701             :  *
    2702             :  * @note Driver implementation: this method shall be implemented if nodata
    2703             :  * is supported.
    2704             :  *
    2705             :  * This is the same as the C function GDALMDArrayGetRawNoDataValue().
    2706             :  *
    2707             :  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
    2708             :  */
    2709           5 : const void *GDALMDArray::GetRawNoDataValue() const
    2710             : {
    2711           5 :     return nullptr;
    2712             : }
    2713             : 
    2714             : /************************************************************************/
    2715             : /*                        GetNoDataValueAsDouble()                      */
    2716             : /************************************************************************/
    2717             : 
    2718             : /** Return the nodata value as a double.
    2719             :  *
    2720             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
    2721             :  *
    2722             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2723             :  * a nodata value exists and can be converted to double. Might be nullptr.
    2724             :  *
    2725             :  * @return the nodata value as a double. A 0.0 value might also indicate the
    2726             :  * absence of a nodata value or an error in the conversion (*pbHasNoData will be
    2727             :  * set to false then).
    2728             :  */
    2729       22506 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
    2730             : {
    2731       22506 :     const void *pNoData = GetRawNoDataValue();
    2732       22506 :     double dfNoData = 0.0;
    2733       22506 :     const auto &eDT = GetDataType();
    2734       22506 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2735       22506 :     if (ok)
    2736             :     {
    2737       22194 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
    2738             :                         GDT_Float64, 0, 1);
    2739             :     }
    2740       22506 :     if (pbHasNoData)
    2741         465 :         *pbHasNoData = ok;
    2742       22506 :     return dfNoData;
    2743             : }
    2744             : 
    2745             : /************************************************************************/
    2746             : /*                        GetNoDataValueAsInt64()                       */
    2747             : /************************************************************************/
    2748             : 
    2749             : /** Return the nodata value as a Int64.
    2750             :  *
    2751             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2752             :  * a nodata value exists and can be converted to Int64. Might be nullptr.
    2753             :  *
    2754             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
    2755             :  *
    2756             :  * @return the nodata value as a Int64
    2757             :  *
    2758             :  * @since GDAL 3.5
    2759             :  */
    2760          12 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
    2761             : {
    2762          12 :     const void *pNoData = GetRawNoDataValue();
    2763          12 :     int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
    2764          12 :     const auto &eDT = GetDataType();
    2765          12 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2766          12 :     if (ok)
    2767             :     {
    2768           8 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
    2769             :                         GDT_Int64, 0, 1);
    2770             :     }
    2771          12 :     if (pbHasNoData)
    2772          12 :         *pbHasNoData = ok;
    2773          12 :     return nNoData;
    2774             : }
    2775             : 
    2776             : /************************************************************************/
    2777             : /*                       GetNoDataValueAsUInt64()                       */
    2778             : /************************************************************************/
    2779             : 
    2780             : /** Return the nodata value as a UInt64.
    2781             :  *
    2782             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
    2783             : 
    2784             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2785             :  * a nodata value exists and can be converted to UInt64. Might be nullptr.
    2786             :  *
    2787             :  * @return the nodata value as a UInt64
    2788             :  *
    2789             :  * @since GDAL 3.5
    2790             :  */
    2791           8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
    2792             : {
    2793           8 :     const void *pNoData = GetRawNoDataValue();
    2794           8 :     uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
    2795           8 :     const auto &eDT = GetDataType();
    2796           8 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2797           8 :     if (ok)
    2798             :     {
    2799           6 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
    2800             :                         GDT_UInt64, 0, 1);
    2801             :     }
    2802           8 :     if (pbHasNoData)
    2803           8 :         *pbHasNoData = ok;
    2804           8 :     return nNoData;
    2805             : }
    2806             : 
    2807             : /************************************************************************/
    2808             : /*                        SetRawNoDataValue()                           */
    2809             : /************************************************************************/
    2810             : 
    2811             : /** Set the nodata value as a "raw" value.
    2812             :  *
    2813             :  * The value passed might be nullptr in case of no nodata value. When
    2814             :  * a nodata value is registered, a non-nullptr whose size in
    2815             :  * bytes is GetDataType().GetSize() must be passed.
    2816             :  *
    2817             :  * This is the same as the C function GDALMDArraySetRawNoDataValue().
    2818             :  *
    2819             :  * @note Driver implementation: this method shall be implemented if setting
    2820             :  nodata
    2821             :  * is supported.
    2822             : 
    2823             :  * @return true in case of success.
    2824             :  */
    2825           0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
    2826             : {
    2827           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2828             :              "SetRawNoDataValue() not implemented");
    2829           0 :     return false;
    2830             : }
    2831             : 
    2832             : /************************************************************************/
    2833             : /*                           SetNoDataValue()                           */
    2834             : /************************************************************************/
    2835             : 
    2836             : /** Set the nodata value as a double.
    2837             :  *
    2838             :  * If the natural data type of the attribute/array is not double, type
    2839             :  * conversion will occur to the type returned by GetDataType().
    2840             :  *
    2841             :  * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
    2842             :  *
    2843             :  * @return true in case of success.
    2844             :  */
    2845          61 : bool GDALMDArray::SetNoDataValue(double dfNoData)
    2846             : {
    2847          61 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2848          61 :     bool bRet = false;
    2849          61 :     if (GDALExtendedDataType::CopyValue(
    2850         122 :             &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
    2851          61 :             GetDataType()))
    2852             :     {
    2853          61 :         bRet = SetRawNoDataValue(pRawNoData);
    2854             :     }
    2855          61 :     CPLFree(pRawNoData);
    2856          61 :     return bRet;
    2857             : }
    2858             : 
    2859             : /************************************************************************/
    2860             : /*                           SetNoDataValue()                           */
    2861             : /************************************************************************/
    2862             : 
    2863             : /** Set the nodata value as a Int64.
    2864             :  *
    2865             :  * If the natural data type of the attribute/array is not Int64, type conversion
    2866             :  * will occur to the type returned by GetDataType().
    2867             :  *
    2868             :  * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
    2869             :  *
    2870             :  * @return true in case of success.
    2871             :  *
    2872             :  * @since GDAL 3.5
    2873             :  */
    2874           3 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
    2875             : {
    2876           3 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2877           3 :     bool bRet = false;
    2878           3 :     if (GDALExtendedDataType::CopyValue(&nNoData,
    2879           6 :                                         GDALExtendedDataType::Create(GDT_Int64),
    2880           3 :                                         pRawNoData, GetDataType()))
    2881             :     {
    2882           3 :         bRet = SetRawNoDataValue(pRawNoData);
    2883             :     }
    2884           3 :     CPLFree(pRawNoData);
    2885           3 :     return bRet;
    2886             : }
    2887             : 
    2888             : /************************************************************************/
    2889             : /*                           SetNoDataValue()                           */
    2890             : /************************************************************************/
    2891             : 
    2892             : /** Set the nodata value as a Int64.
    2893             :  *
    2894             :  * If the natural data type of the attribute/array is not Int64, type conversion
    2895             :  * will occur to the type returned by GetDataType().
    2896             :  *
    2897             :  * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
    2898             :  *
    2899             :  * @return true in case of success.
    2900             :  *
    2901             :  * @since GDAL 3.5
    2902             :  */
    2903           1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
    2904             : {
    2905           1 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2906           1 :     bool bRet = false;
    2907           1 :     if (GDALExtendedDataType::CopyValue(
    2908           2 :             &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
    2909           1 :             GetDataType()))
    2910             :     {
    2911           1 :         bRet = SetRawNoDataValue(pRawNoData);
    2912             :     }
    2913           1 :     CPLFree(pRawNoData);
    2914           1 :     return bRet;
    2915             : }
    2916             : 
    2917             : /************************************************************************/
    2918             : /*                            Resize()                                  */
    2919             : /************************************************************************/
    2920             : 
    2921             : /** Resize an array to new dimensions.
    2922             :  *
    2923             :  * Not all drivers may allow this operation, and with restrictions (e.g.
    2924             :  * for netCDF, this is limited to growing of "unlimited" dimensions)
    2925             :  *
    2926             :  * Resizing a dimension used in other arrays will cause those other arrays
    2927             :  * to be resized.
    2928             :  *
    2929             :  * This is the same as the C function GDALMDArrayResize().
    2930             :  *
    2931             :  * @param anNewDimSizes Array of GetDimensionCount() values containing the
    2932             :  *                      new size of each indexing dimension.
    2933             :  * @param papszOptions Options. (Driver specific)
    2934             :  * @return true in case of success.
    2935             :  * @since GDAL 3.7
    2936             :  */
    2937           0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
    2938             :                          CPL_UNUSED CSLConstList papszOptions)
    2939             : {
    2940           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2941             :              "Resize() is not supported for this array");
    2942           0 :     return false;
    2943             : }
    2944             : 
    2945             : /************************************************************************/
    2946             : /*                               SetScale()                             */
    2947             : /************************************************************************/
    2948             : 
    2949             : /** Set the scale value to apply to raw values.
    2950             :  *
    2951             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2952             :  *
    2953             :  * This is the same as the C function GDALMDArraySetScale() /
    2954             :  * GDALMDArraySetScaleEx().
    2955             :  *
    2956             :  * @note Driver implementation: this method shall be implemented if setting
    2957             :  * scale is supported.
    2958             :  *
    2959             :  * @param dfScale scale
    2960             :  * @param eStorageType Data type to which create the potential attribute that
    2961             :  * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
    2962             :  * implementation will decide automatically the data type. Note that changing
    2963             :  * the data type after initial setting might not be supported.
    2964             :  * @return true in case of success.
    2965             :  */
    2966           0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
    2967             :                            CPL_UNUSED GDALDataType eStorageType)
    2968             : {
    2969           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
    2970           0 :     return false;
    2971             : }
    2972             : 
    2973             : /************************************************************************/
    2974             : /*                               SetOffset)                             */
    2975             : /************************************************************************/
    2976             : 
    2977             : /** Set the offset value to apply to raw values.
    2978             :  *
    2979             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2980             :  *
    2981             :  * This is the same as the C function GDALMDArraySetOffset() /
    2982             :  * GDALMDArraySetOffsetEx().
    2983             :  *
    2984             :  * @note Driver implementation: this method shall be implemented if setting
    2985             :  * offset is supported.
    2986             :  *
    2987             :  * @param dfOffset Offset
    2988             :  * @param eStorageType Data type to which create the potential attribute that
    2989             :  * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
    2990             :  * implementation will decide automatically the data type. Note that changing
    2991             :  * the data type after initial setting might not be supported.
    2992             :  * @return true in case of success.
    2993             :  */
    2994           0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
    2995             :                             CPL_UNUSED GDALDataType eStorageType)
    2996             : {
    2997           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
    2998           0 :     return false;
    2999             : }
    3000             : 
    3001             : /************************************************************************/
    3002             : /*                               GetScale()                             */
    3003             : /************************************************************************/
    3004             : 
    3005             : /** Get the scale value to apply to raw values.
    3006             :  *
    3007             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    3008             :  *
    3009             :  * This is the same as the C function GDALMDArrayGetScale().
    3010             :  *
    3011             :  * @note Driver implementation: this method shall be implemented if getting
    3012             :  * scale is supported.
    3013             :  *
    3014             :  * @param pbHasScale Pointer to a output boolean that will be set to true if
    3015             :  * a scale value exists. Might be nullptr.
    3016             :  * @param peStorageType Pointer to a output GDALDataType that will be set to
    3017             :  * the storage type of the scale value, when known/relevant. Otherwise will be
    3018             :  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
    3019             :  *
    3020             :  * @return the scale value. A 1.0 value might also indicate the
    3021             :  * absence of a scale value.
    3022             :  */
    3023          20 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
    3024             :                              CPL_UNUSED GDALDataType *peStorageType) const
    3025             : {
    3026          20 :     if (pbHasScale)
    3027          20 :         *pbHasScale = false;
    3028          20 :     return 1.0;
    3029             : }
    3030             : 
    3031             : /************************************************************************/
    3032             : /*                               GetOffset()                            */
    3033             : /************************************************************************/
    3034             : 
    3035             : /** Get the offset value to apply to raw values.
    3036             :  *
    3037             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    3038             :  *
    3039             :  * This is the same as the C function GDALMDArrayGetOffset().
    3040             :  *
    3041             :  * @note Driver implementation: this method shall be implemented if getting
    3042             :  * offset is supported.
    3043             :  *
    3044             :  * @param pbHasOffset Pointer to a output boolean that will be set to true if
    3045             :  * a offset value exists. Might be nullptr.
    3046             :  * @param peStorageType Pointer to a output GDALDataType that will be set to
    3047             :  * the storage type of the offset value, when known/relevant. Otherwise will be
    3048             :  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
    3049             :  *
    3050             :  * @return the offset value. A 0.0 value might also indicate the
    3051             :  * absence of a offset value.
    3052             :  */
    3053          20 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
    3054             :                               CPL_UNUSED GDALDataType *peStorageType) const
    3055             : {
    3056          20 :     if (pbHasOffset)
    3057          20 :         *pbHasOffset = false;
    3058          20 :     return 0.0;
    3059             : }
    3060             : 
    3061             : /************************************************************************/
    3062             : /*                         ProcessPerChunk()                            */
    3063             : /************************************************************************/
    3064             : 
    3065             : namespace
    3066             : {
    3067             : enum class Caller
    3068             : {
    3069             :     CALLER_END_OF_LOOP,
    3070             :     CALLER_IN_LOOP,
    3071             : };
    3072             : }
    3073             : 
    3074             : /** \brief Call a user-provided function to operate on an array chunk by chunk.
    3075             :  *
    3076             :  * This method is to be used when doing operations on an array, or a subset of
    3077             :  * it, in a chunk by chunk way.
    3078             :  *
    3079             :  * @param arrayStartIdx Values representing the starting index to use
    3080             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    3081             :  *                      Array of GetDimensionCount() values. Must not be
    3082             :  *                      nullptr, unless for a zero-dimensional array.
    3083             :  *
    3084             :  * @param count         Values representing the number of values to use in
    3085             :  *                      each dimension.
    3086             :  *                      Array of GetDimensionCount() values. Must not be
    3087             :  *                      nullptr, unless for a zero-dimensional array.
    3088             :  *
    3089             :  * @param chunkSize     Values representing the chunk size in each dimension.
    3090             :  *                      Might typically the output of GetProcessingChunkSize().
    3091             :  *                      Array of GetDimensionCount() values. Must not be
    3092             :  *                      nullptr, unless for a zero-dimensional array.
    3093             :  *
    3094             :  * @param pfnFunc       User-provided function of type FuncProcessPerChunkType.
    3095             :  *                      Must NOT be nullptr.
    3096             :  *
    3097             :  * @param pUserData     Pointer to pass as the value of the pUserData argument
    3098             :  * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
    3099             :  *
    3100             :  * @return true in case of success.
    3101             :  */
    3102          88 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
    3103             :                                           const GUInt64 *count,
    3104             :                                           const size_t *chunkSize,
    3105             :                                           FuncProcessPerChunkType pfnFunc,
    3106             :                                           void *pUserData)
    3107             : {
    3108          88 :     const auto &dims = GetDimensions();
    3109          88 :     if (dims.empty())
    3110             :     {
    3111           2 :         return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
    3112             :     }
    3113             : 
    3114             :     // Sanity check
    3115          86 :     size_t nTotalChunkSize = 1;
    3116         219 :     for (size_t i = 0; i < dims.size(); i++)
    3117             :     {
    3118         140 :         const auto nSizeThisDim(dims[i]->GetSize());
    3119         140 :         if (count[i] == 0 || count[i] > nSizeThisDim ||
    3120         138 :             arrayStartIdx[i] > nSizeThisDim - count[i])
    3121             :         {
    3122           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    3123             :                      "Inconsistent arrayStartIdx[] / count[] values "
    3124             :                      "regarding array size");
    3125           4 :             return false;
    3126             :         }
    3127         270 :         if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
    3128         134 :             chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
    3129             :         {
    3130           3 :             CPLError(CE_Failure, CPLE_AppDefined,
    3131             :                      "Inconsistent chunkSize[] values");
    3132           3 :             return false;
    3133             :         }
    3134         133 :         nTotalChunkSize *= chunkSize[i];
    3135             :     }
    3136             : 
    3137          79 :     size_t dimIdx = 0;
    3138         158 :     std::vector<GUInt64> chunkArrayStartIdx(dims.size());
    3139         158 :     std::vector<size_t> chunkCount(dims.size());
    3140             : 
    3141             :     struct Stack
    3142             :     {
    3143             :         GUInt64 nBlockCounter = 0;
    3144             :         GUInt64 nBlocksMinusOne = 0;
    3145             :         size_t first_count = 0;  // only used if nBlocks > 1
    3146             :         Caller return_point = Caller::CALLER_END_OF_LOOP;
    3147             :     };
    3148             : 
    3149         158 :     std::vector<Stack> stack(dims.size());
    3150          79 :     GUInt64 iCurChunk = 0;
    3151          79 :     GUInt64 nChunkCount = 1;
    3152         211 :     for (size_t i = 0; i < dims.size(); i++)
    3153             :     {
    3154         132 :         const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
    3155         132 :         const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
    3156         132 :         stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
    3157         132 :         nChunkCount *= 1 + stack[i].nBlocksMinusOne;
    3158         132 :         if (stack[i].nBlocksMinusOne == 0)
    3159             :         {
    3160         127 :             chunkArrayStartIdx[i] = arrayStartIdx[i];
    3161         127 :             chunkCount[i] = static_cast<size_t>(count[i]);
    3162             :         }
    3163             :         else
    3164             :         {
    3165           5 :             stack[i].first_count = static_cast<size_t>(
    3166           5 :                 (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
    3167             :         }
    3168             :     }
    3169             : 
    3170          79 : lbl_next_depth:
    3171         321 :     if (dimIdx == dims.size())
    3172             :     {
    3173         112 :         ++iCurChunk;
    3174         112 :         if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
    3175             :                      iCurChunk, nChunkCount, pUserData))
    3176             :         {
    3177           1 :             return false;
    3178             :         }
    3179             :     }
    3180             :     else
    3181             :     {
    3182         209 :         if (stack[dimIdx].nBlocksMinusOne != 0)
    3183             :         {
    3184          11 :             stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
    3185          11 :             chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
    3186          11 :             chunkCount[dimIdx] = stack[dimIdx].first_count;
    3187          11 :             stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
    3188             :             while (true)
    3189             :             {
    3190          33 :                 dimIdx++;
    3191          33 :                 goto lbl_next_depth;
    3192          33 :             lbl_return_to_caller_in_loop:
    3193          33 :                 --stack[dimIdx].nBlockCounter;
    3194          33 :                 if (stack[dimIdx].nBlockCounter == 0)
    3195          11 :                     break;
    3196          22 :                 chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    3197          22 :                 chunkCount[dimIdx] = chunkSize[dimIdx];
    3198             :             }
    3199             : 
    3200          11 :             chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    3201          22 :             chunkCount[dimIdx] =
    3202          11 :                 static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
    3203          11 :                                     chunkArrayStartIdx[dimIdx]);
    3204          11 :             stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
    3205             :         }
    3206         209 :         dimIdx++;
    3207         209 :         goto lbl_next_depth;
    3208         207 :     lbl_return_to_caller_end_of_loop:
    3209         207 :         if (dimIdx == 0)
    3210          78 :             goto end;
    3211             :     }
    3212             : 
    3213         240 :     assert(dimIdx > 0);
    3214         240 :     dimIdx--;
    3215             :     // cppcheck-suppress negativeContainerIndex
    3216         240 :     switch (stack[dimIdx].return_point)
    3217             :     {
    3218         207 :         case Caller::CALLER_END_OF_LOOP:
    3219         207 :             goto lbl_return_to_caller_end_of_loop;
    3220          33 :         case Caller::CALLER_IN_LOOP:
    3221          33 :             goto lbl_return_to_caller_in_loop;
    3222             :     }
    3223          78 : end:
    3224          78 :     return true;
    3225             : }
    3226             : 
    3227             : /************************************************************************/
    3228             : /*                          GDALAttribute()                             */
    3229             : /************************************************************************/
    3230             : 
    3231             : //! @cond Doxygen_Suppress
    3232       28441 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
    3233           0 :                              CPL_UNUSED const std::string &osName)
    3234             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
    3235       28441 :     : GDALAbstractMDArray(osParentName, osName)
    3236             : #endif
    3237             : {
    3238       28441 : }
    3239             : 
    3240             : GDALAttribute::~GDALAttribute() = default;
    3241             : 
    3242             : //! @endcond
    3243             : 
    3244             : /************************************************************************/
    3245             : /*                        GetDimensionSize()                            */
    3246             : /************************************************************************/
    3247             : 
    3248             : /** Return the size of the dimensions of the attribute.
    3249             :  *
    3250             :  * This will be an empty array for a scalar (single value) attribute.
    3251             :  *
    3252             :  * This is the same as the C function GDALAttributeGetDimensionsSize().
    3253             :  */
    3254         655 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
    3255             : {
    3256         655 :     const auto &dims = GetDimensions();
    3257         655 :     std::vector<GUInt64> ret;
    3258         655 :     ret.reserve(dims.size());
    3259         805 :     for (const auto &dim : dims)
    3260         150 :         ret.push_back(dim->GetSize());
    3261         655 :     return ret;
    3262             : }
    3263             : 
    3264             : /************************************************************************/
    3265             : /*                            GDALRawResult()                           */
    3266             : /************************************************************************/
    3267             : 
    3268             : //! @cond Doxygen_Suppress
    3269         214 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
    3270         214 :                              size_t nEltCount)
    3271         428 :     : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
    3272         214 :       m_raw(raw)
    3273             : {
    3274         214 : }
    3275             : 
    3276             : //! @endcond
    3277             : 
    3278             : /************************************************************************/
    3279             : /*                            GDALRawResult()                           */
    3280             : /************************************************************************/
    3281             : 
    3282             : /** Move constructor. */
    3283           0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
    3284           0 :     : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
    3285           0 :       m_nSize(other.m_nSize), m_raw(other.m_raw)
    3286             : {
    3287           0 :     other.m_nEltCount = 0;
    3288           0 :     other.m_nSize = 0;
    3289           0 :     other.m_raw = nullptr;
    3290           0 : }
    3291             : 
    3292             : /************************************************************************/
    3293             : /*                               FreeMe()                               */
    3294             : /************************************************************************/
    3295             : 
    3296         214 : void GDALRawResult::FreeMe()
    3297             : {
    3298         214 :     if (m_raw && m_dt.NeedsFreeDynamicMemory())
    3299             :     {
    3300         103 :         GByte *pabyPtr = m_raw;
    3301         103 :         const auto nDTSize(m_dt.GetSize());
    3302         206 :         for (size_t i = 0; i < m_nEltCount; ++i)
    3303             :         {
    3304         103 :             m_dt.FreeDynamicMemory(pabyPtr);
    3305         103 :             pabyPtr += nDTSize;
    3306             :         }
    3307             :     }
    3308         214 :     VSIFree(m_raw);
    3309         214 : }
    3310             : 
    3311             : /************************************************************************/
    3312             : /*                             operator=()                              */
    3313             : /************************************************************************/
    3314             : 
    3315             : /** Move assignment. */
    3316           0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
    3317             : {
    3318           0 :     FreeMe();
    3319           0 :     m_dt = std::move(other.m_dt);
    3320           0 :     m_nEltCount = other.m_nEltCount;
    3321           0 :     m_nSize = other.m_nSize;
    3322           0 :     m_raw = other.m_raw;
    3323           0 :     other.m_nEltCount = 0;
    3324           0 :     other.m_nSize = 0;
    3325           0 :     other.m_raw = nullptr;
    3326           0 :     return *this;
    3327             : }
    3328             : 
    3329             : /************************************************************************/
    3330             : /*                         ~GDALRawResult()                             */
    3331             : /************************************************************************/
    3332             : 
    3333             : /** Destructor. */
    3334         214 : GDALRawResult::~GDALRawResult()
    3335             : {
    3336         214 :     FreeMe();
    3337         214 : }
    3338             : 
    3339             : /************************************************************************/
    3340             : /*                            StealData()                               */
    3341             : /************************************************************************/
    3342             : 
    3343             : //! @cond Doxygen_Suppress
    3344             : /** Return buffer to caller which becomes owner of it.
    3345             :  * Only to be used by GDALAttributeReadAsRaw().
    3346             :  */
    3347           6 : GByte *GDALRawResult::StealData()
    3348             : {
    3349           6 :     GByte *ret = m_raw;
    3350           6 :     m_raw = nullptr;
    3351           6 :     m_nEltCount = 0;
    3352           6 :     m_nSize = 0;
    3353           6 :     return ret;
    3354             : }
    3355             : 
    3356             : //! @endcond
    3357             : 
    3358             : /************************************************************************/
    3359             : /*                             ReadAsRaw()                              */
    3360             : /************************************************************************/
    3361             : 
    3362             : /** Return the raw value of an attribute.
    3363             :  *
    3364             :  *
    3365             :  * This is the same as the C function GDALAttributeReadAsRaw().
    3366             :  */
    3367         214 : GDALRawResult GDALAttribute::ReadAsRaw() const
    3368             : {
    3369         214 :     const auto nEltCount(GetTotalElementsCount());
    3370         214 :     const auto &dt(GetDataType());
    3371         214 :     const auto nDTSize(dt.GetSize());
    3372             :     GByte *res = static_cast<GByte *>(
    3373         214 :         VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
    3374         214 :     if (!res)
    3375           0 :         return GDALRawResult(nullptr, dt, 0);
    3376         214 :     const auto &dims = GetDimensions();
    3377         214 :     const auto nDims = GetDimensionCount();
    3378         428 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3379         428 :     std::vector<size_t> count(1 + nDims);
    3380         237 :     for (size_t i = 0; i < nDims; i++)
    3381             :     {
    3382          23 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3383             :     }
    3384         214 :     if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
    3385         214 :               &res[0], static_cast<size_t>(nEltCount * nDTSize)))
    3386             :     {
    3387           0 :         VSIFree(res);
    3388           0 :         return GDALRawResult(nullptr, dt, 0);
    3389             :     }
    3390         214 :     return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
    3391             : }
    3392             : 
    3393             : /************************************************************************/
    3394             : /*                            ReadAsString()                            */
    3395             : /************************************************************************/
    3396             : 
    3397             : /** Return the value of an attribute as a string.
    3398             :  *
    3399             :  * The returned string should not be freed, and its lifetime does not
    3400             :  * excess a next call to ReadAsString() on the same object, or the deletion
    3401             :  * of the object itself.
    3402             :  *
    3403             :  * This function will only return the first element if there are several.
    3404             :  *
    3405             :  * This is the same as the C function GDALAttributeReadAsString()
    3406             :  *
    3407             :  * @return a string, or nullptr.
    3408             :  */
    3409        1904 : const char *GDALAttribute::ReadAsString() const
    3410             : {
    3411        1904 :     const auto nDims = GetDimensionCount();
    3412        3808 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3413        3808 :     std::vector<size_t> count(1 + nDims, 1);
    3414        1904 :     char *szRet = nullptr;
    3415        1904 :     if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
    3416        1904 :               GDALExtendedDataType::CreateString(), &szRet, &szRet,
    3417        5711 :               sizeof(szRet)) ||
    3418        1903 :         szRet == nullptr)
    3419             :     {
    3420           5 :         return nullptr;
    3421             :     }
    3422        1899 :     m_osCachedVal = szRet;
    3423        1899 :     CPLFree(szRet);
    3424        1899 :     return m_osCachedVal.c_str();
    3425             : }
    3426             : 
    3427             : /************************************************************************/
    3428             : /*                            ReadAsInt()                               */
    3429             : /************************************************************************/
    3430             : 
    3431             : /** Return the value of an attribute as a integer.
    3432             :  *
    3433             :  * This function will only return the first element if there are several.
    3434             :  *
    3435             :  * It can fail if its value can not be converted to integer.
    3436             :  *
    3437             :  * This is the same as the C function GDALAttributeReadAsInt()
    3438             :  *
    3439             :  * @return a integer, or INT_MIN in case of error.
    3440             :  */
    3441         514 : int GDALAttribute::ReadAsInt() const
    3442             : {
    3443         514 :     const auto nDims = GetDimensionCount();
    3444        1028 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3445         514 :     std::vector<size_t> count(1 + nDims, 1);
    3446         514 :     int nRet = INT_MIN;
    3447         514 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3448        1028 :          GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
    3449        1028 :     return nRet;
    3450             : }
    3451             : 
    3452             : /************************************************************************/
    3453             : /*                            ReadAsInt64()                             */
    3454             : /************************************************************************/
    3455             : 
    3456             : /** Return the value of an attribute as an int64_t.
    3457             :  *
    3458             :  * This function will only return the first element if there are several.
    3459             :  *
    3460             :  * It can fail if its value can not be converted to long.
    3461             :  *
    3462             :  * This is the same as the C function GDALAttributeReadAsInt64()
    3463             :  *
    3464             :  * @return an int64_t, or INT64_MIN in case of error.
    3465             :  */
    3466         102 : int64_t GDALAttribute::ReadAsInt64() const
    3467             : {
    3468         102 :     const auto nDims = GetDimensionCount();
    3469         204 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3470         102 :     std::vector<size_t> count(1 + nDims, 1);
    3471         102 :     int64_t nRet = INT64_MIN;
    3472         102 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3473         204 :          GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
    3474         204 :     return nRet;
    3475             : }
    3476             : 
    3477             : /************************************************************************/
    3478             : /*                            ReadAsDouble()                            */
    3479             : /************************************************************************/
    3480             : 
    3481             : /** Return the value of an attribute as a double.
    3482             :  *
    3483             :  * This function will only return the first element if there are several.
    3484             :  *
    3485             :  * It can fail if its value can not be converted to double.
    3486             :  *
    3487             :  * This is the same as the C function GDALAttributeReadAsInt()
    3488             :  *
    3489             :  * @return a double value.
    3490             :  */
    3491         544 : double GDALAttribute::ReadAsDouble() const
    3492             : {
    3493         544 :     const auto nDims = GetDimensionCount();
    3494        1088 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3495         544 :     std::vector<size_t> count(1 + nDims, 1);
    3496         544 :     double dfRet = 0;
    3497         544 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3498         544 :          GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
    3499         544 :          sizeof(dfRet));
    3500        1088 :     return dfRet;
    3501             : }
    3502             : 
    3503             : /************************************************************************/
    3504             : /*                          ReadAsStringArray()                         */
    3505             : /************************************************************************/
    3506             : 
    3507             : /** Return the value of an attribute as an array of strings.
    3508             :  *
    3509             :  * This is the same as the C function GDALAttributeReadAsStringArray()
    3510             :  */
    3511         169 : CPLStringList GDALAttribute::ReadAsStringArray() const
    3512             : {
    3513         169 :     const auto nElts = GetTotalElementsCount();
    3514         169 :     if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
    3515           0 :         return CPLStringList();
    3516             :     char **papszList = static_cast<char **>(
    3517         169 :         VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
    3518         169 :     const auto &dims = GetDimensions();
    3519         169 :     const auto nDims = GetDimensionCount();
    3520         338 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3521         338 :     std::vector<size_t> count(1 + nDims);
    3522         264 :     for (size_t i = 0; i < nDims; i++)
    3523             :     {
    3524          95 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3525             :     }
    3526         169 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3527         169 :          GDALExtendedDataType::CreateString(), papszList, papszList,
    3528         169 :          sizeof(char *) * static_cast<int>(nElts));
    3529         662 :     for (int i = 0; i < static_cast<int>(nElts); i++)
    3530             :     {
    3531         493 :         if (papszList[i] == nullptr)
    3532          13 :             papszList[i] = CPLStrdup("");
    3533             :     }
    3534         169 :     return CPLStringList(papszList);
    3535             : }
    3536             : 
    3537             : /************************************************************************/
    3538             : /*                          ReadAsIntArray()                            */
    3539             : /************************************************************************/
    3540             : 
    3541             : /** Return the value of an attribute as an array of integers.
    3542             :  *
    3543             :  * This is the same as the C function GDALAttributeReadAsIntArray().
    3544             :  */
    3545          15 : std::vector<int> GDALAttribute::ReadAsIntArray() const
    3546             : {
    3547          15 :     const auto nElts = GetTotalElementsCount();
    3548             : #if SIZEOF_VOIDP == 4
    3549             :     if (nElts > static_cast<size_t>(nElts))
    3550             :         return {};
    3551             : #endif
    3552          15 :     std::vector<int> res(static_cast<size_t>(nElts));
    3553          15 :     const auto &dims = GetDimensions();
    3554          15 :     const auto nDims = GetDimensionCount();
    3555          30 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3556          30 :     std::vector<size_t> count(1 + nDims);
    3557          32 :     for (size_t i = 0; i < nDims; i++)
    3558             :     {
    3559          17 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3560             :     }
    3561          15 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3562          30 :          GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
    3563          15 :          res.size() * sizeof(res[0]));
    3564          30 :     return res;
    3565             : }
    3566             : 
    3567             : /************************************************************************/
    3568             : /*                          ReadAsInt64Array()                          */
    3569             : /************************************************************************/
    3570             : 
    3571             : /** Return the value of an attribute as an array of int64_t.
    3572             :  *
    3573             :  * This is the same as the C function GDALAttributeReadAsInt64Array().
    3574             :  */
    3575          62 : std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
    3576             : {
    3577          62 :     const auto nElts = GetTotalElementsCount();
    3578             : #if SIZEOF_VOIDP == 4
    3579             :     if (nElts > static_cast<size_t>(nElts))
    3580             :         return {};
    3581             : #endif
    3582          62 :     std::vector<int64_t> res(static_cast<size_t>(nElts));
    3583          62 :     const auto &dims = GetDimensions();
    3584          62 :     const auto nDims = GetDimensionCount();
    3585         124 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3586         124 :     std::vector<size_t> count(1 + nDims);
    3587         124 :     for (size_t i = 0; i < nDims; i++)
    3588             :     {
    3589          62 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3590             :     }
    3591          62 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3592         124 :          GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
    3593          62 :          res.size() * sizeof(res[0]));
    3594         124 :     return res;
    3595             : }
    3596             : 
    3597             : /************************************************************************/
    3598             : /*                         ReadAsDoubleArray()                          */
    3599             : /************************************************************************/
    3600             : 
    3601             : /** Return the value of an attribute as an array of double.
    3602             :  *
    3603             :  * This is the same as the C function GDALAttributeReadAsDoubleArray().
    3604             :  */
    3605          94 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
    3606             : {
    3607          94 :     const auto nElts = GetTotalElementsCount();
    3608             : #if SIZEOF_VOIDP == 4
    3609             :     if (nElts > static_cast<size_t>(nElts))
    3610             :         return {};
    3611             : #endif
    3612          94 :     std::vector<double> res(static_cast<size_t>(nElts));
    3613          94 :     const auto &dims = GetDimensions();
    3614          94 :     const auto nDims = GetDimensionCount();
    3615         188 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3616         188 :     std::vector<size_t> count(1 + nDims);
    3617         172 :     for (size_t i = 0; i < nDims; i++)
    3618             :     {
    3619          78 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3620             :     }
    3621          94 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3622         188 :          GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
    3623          94 :          res.size() * sizeof(res[0]));
    3624         188 :     return res;
    3625             : }
    3626             : 
    3627             : /************************************************************************/
    3628             : /*                               Write()                                */
    3629             : /************************************************************************/
    3630             : 
    3631             : /** Write an attribute from raw values expressed in GetDataType()
    3632             :  *
    3633             :  * The values should be provided in the type of GetDataType() and there should
    3634             :  * be exactly GetTotalElementsCount() of them.
    3635             :  * If GetDataType() is a string, each value should be a char* pointer.
    3636             :  *
    3637             :  * This is the same as the C function GDALAttributeWriteRaw().
    3638             :  *
    3639             :  * @param pabyValue Buffer of nLen bytes.
    3640             :  * @param nLen Size of pabyValue in bytes. Should be equal to
    3641             :  *             GetTotalElementsCount() * GetDataType().GetSize()
    3642             :  * @return true in case of success.
    3643             :  */
    3644         151 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
    3645             : {
    3646         151 :     if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
    3647             :     {
    3648           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3649             :                  "Length is not of expected value");
    3650           0 :         return false;
    3651             :     }
    3652         151 :     const auto &dims = GetDimensions();
    3653         151 :     const auto nDims = GetDimensionCount();
    3654         302 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3655         302 :     std::vector<size_t> count(1 + nDims);
    3656         174 :     for (size_t i = 0; i < nDims; i++)
    3657             :     {
    3658          23 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3659             :     }
    3660         151 :     return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
    3661         151 :                  pabyValue, pabyValue, nLen);
    3662             : }
    3663             : 
    3664             : /************************************************************************/
    3665             : /*                               Write()                                */
    3666             : /************************************************************************/
    3667             : 
    3668             : /** Write an attribute from a string value.
    3669             :  *
    3670             :  * Type conversion will be performed if needed. If the attribute contains
    3671             :  * multiple values, only the first one will be updated.
    3672             :  *
    3673             :  * This is the same as the C function GDALAttributeWriteString().
    3674             :  *
    3675             :  * @param pszValue Pointer to a string.
    3676             :  * @return true in case of success.
    3677             :  */
    3678         391 : bool GDALAttribute::Write(const char *pszValue)
    3679             : {
    3680         391 :     const auto nDims = GetDimensionCount();
    3681         782 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3682         391 :     std::vector<size_t> count(1 + nDims, 1);
    3683         391 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3684         782 :                  GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
    3685         782 :                  sizeof(pszValue));
    3686             : }
    3687             : 
    3688             : /************************************************************************/
    3689             : /*                              WriteInt()                              */
    3690             : /************************************************************************/
    3691             : 
    3692             : /** Write an attribute from a integer value.
    3693             :  *
    3694             :  * Type conversion will be performed if needed. If the attribute contains
    3695             :  * multiple values, only the first one will be updated.
    3696             :  *
    3697             :  * This is the same as the C function GDALAttributeWriteInt().
    3698             :  *
    3699             :  * @param nVal Value.
    3700             :  * @return true in case of success.
    3701             :  */
    3702          22 : bool GDALAttribute::WriteInt(int nVal)
    3703             : {
    3704          22 :     const auto nDims = GetDimensionCount();
    3705          44 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3706          22 :     std::vector<size_t> count(1 + nDims, 1);
    3707          22 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3708          44 :                  GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
    3709          44 :                  sizeof(nVal));
    3710             : }
    3711             : 
    3712             : /************************************************************************/
    3713             : /*                              WriteInt64()                             */
    3714             : /************************************************************************/
    3715             : 
    3716             : /** Write an attribute from an int64_t value.
    3717             :  *
    3718             :  * Type conversion will be performed if needed. If the attribute contains
    3719             :  * multiple values, only the first one will be updated.
    3720             :  *
    3721             :  * This is the same as the C function GDALAttributeWriteInt().
    3722             :  *
    3723             :  * @param nVal Value.
    3724             :  * @return true in case of success.
    3725             :  */
    3726          11 : bool GDALAttribute::WriteInt64(int64_t nVal)
    3727             : {
    3728          11 :     const auto nDims = GetDimensionCount();
    3729          22 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3730          11 :     std::vector<size_t> count(1 + nDims, 1);
    3731          11 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3732          22 :                  GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
    3733          22 :                  sizeof(nVal));
    3734             : }
    3735             : 
    3736             : /************************************************************************/
    3737             : /*                                Write()                               */
    3738             : /************************************************************************/
    3739             : 
    3740             : /** Write an attribute from a double value.
    3741             :  *
    3742             :  * Type conversion will be performed if needed. If the attribute contains
    3743             :  * multiple values, only the first one will be updated.
    3744             :  *
    3745             :  * This is the same as the C function GDALAttributeWriteDouble().
    3746             :  *
    3747             :  * @param dfVal Value.
    3748             :  * @return true in case of success.
    3749             :  */
    3750          39 : bool GDALAttribute::Write(double dfVal)
    3751             : {
    3752          39 :     const auto nDims = GetDimensionCount();
    3753          78 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3754          39 :     std::vector<size_t> count(1 + nDims, 1);
    3755          39 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3756          78 :                  GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
    3757          78 :                  sizeof(dfVal));
    3758             : }
    3759             : 
    3760             : /************************************************************************/
    3761             : /*                                Write()                               */
    3762             : /************************************************************************/
    3763             : 
    3764             : /** Write an attribute from an array of strings.
    3765             :  *
    3766             :  * Type conversion will be performed if needed.
    3767             :  *
    3768             :  * Exactly GetTotalElementsCount() strings must be provided
    3769             :  *
    3770             :  * This is the same as the C function GDALAttributeWriteStringArray().
    3771             :  *
    3772             :  * @param vals Array of strings.
    3773             :  * @return true in case of success.
    3774             :  */
    3775           8 : bool GDALAttribute::Write(CSLConstList vals)
    3776             : {
    3777           8 :     if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
    3778             :     {
    3779           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3780           1 :         return false;
    3781             :     }
    3782           7 :     const auto nDims = GetDimensionCount();
    3783          14 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3784           7 :     std::vector<size_t> count(1 + nDims);
    3785           7 :     const auto &dims = GetDimensions();
    3786          15 :     for (size_t i = 0; i < nDims; i++)
    3787           8 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3788           7 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3789           7 :                  GDALExtendedDataType::CreateString(), vals, vals,
    3790          14 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
    3791             : }
    3792             : 
    3793             : /************************************************************************/
    3794             : /*                                Write()                               */
    3795             : /************************************************************************/
    3796             : 
    3797             : /** Write an attribute from an array of int.
    3798             :  *
    3799             :  * Type conversion will be performed if needed.
    3800             :  *
    3801             :  * Exactly GetTotalElementsCount() strings must be provided
    3802             :  *
    3803             :  * This is the same as the C function GDALAttributeWriteIntArray()
    3804             :  *
    3805             :  * @param vals Array of int.
    3806             :  * @param nVals Should be equal to GetTotalElementsCount().
    3807             :  * @return true in case of success.
    3808             :  */
    3809          11 : bool GDALAttribute::Write(const int *vals, size_t nVals)
    3810             : {
    3811          11 :     if (nVals != GetTotalElementsCount())
    3812             :     {
    3813           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3814           1 :         return false;
    3815             :     }
    3816          10 :     const auto nDims = GetDimensionCount();
    3817          20 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3818          10 :     std::vector<size_t> count(1 + nDims);
    3819          10 :     const auto &dims = GetDimensions();
    3820          20 :     for (size_t i = 0; i < nDims; i++)
    3821          10 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3822          10 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3823          10 :                  GDALExtendedDataType::Create(GDT_Int32), vals, vals,
    3824          20 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
    3825             : }
    3826             : 
    3827             : /************************************************************************/
    3828             : /*                                Write()                               */
    3829             : /************************************************************************/
    3830             : 
    3831             : /** Write an attribute from an array of int64_t.
    3832             :  *
    3833             :  * Type conversion will be performed if needed.
    3834             :  *
    3835             :  * Exactly GetTotalElementsCount() strings must be provided
    3836             :  *
    3837             :  * This is the same as the C function GDALAttributeWriteLongArray()
    3838             :  *
    3839             :  * @param vals Array of int64_t.
    3840             :  * @param nVals Should be equal to GetTotalElementsCount().
    3841             :  * @return true in case of success.
    3842             :  */
    3843          10 : bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
    3844             : {
    3845          10 :     if (nVals != GetTotalElementsCount())
    3846             :     {
    3847           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3848           0 :         return false;
    3849             :     }
    3850          10 :     const auto nDims = GetDimensionCount();
    3851          20 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3852          10 :     std::vector<size_t> count(1 + nDims);
    3853          10 :     const auto &dims = GetDimensions();
    3854          20 :     for (size_t i = 0; i < nDims; i++)
    3855          10 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3856          10 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3857          10 :                  GDALExtendedDataType::Create(GDT_Int64), vals, vals,
    3858          10 :                  static_cast<size_t>(GetTotalElementsCount()) *
    3859          10 :                      sizeof(int64_t));
    3860             : }
    3861             : 
    3862             : /************************************************************************/
    3863             : /*                                Write()                               */
    3864             : /************************************************************************/
    3865             : 
    3866             : /** Write an attribute from an array of double.
    3867             :  *
    3868             :  * Type conversion will be performed if needed.
    3869             :  *
    3870             :  * Exactly GetTotalElementsCount() strings must be provided
    3871             :  *
    3872             :  * This is the same as the C function GDALAttributeWriteDoubleArray()
    3873             :  *
    3874             :  * @param vals Array of double.
    3875             :  * @param nVals Should be equal to GetTotalElementsCount().
    3876             :  * @return true in case of success.
    3877             :  */
    3878           7 : bool GDALAttribute::Write(const double *vals, size_t nVals)
    3879             : {
    3880           7 :     if (nVals != GetTotalElementsCount())
    3881             :     {
    3882           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3883           1 :         return false;
    3884             :     }
    3885           6 :     const auto nDims = GetDimensionCount();
    3886          12 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3887           6 :     std::vector<size_t> count(1 + nDims);
    3888           6 :     const auto &dims = GetDimensions();
    3889          13 :     for (size_t i = 0; i < nDims; i++)
    3890           7 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3891           6 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3892           6 :                  GDALExtendedDataType::Create(GDT_Float64), vals, vals,
    3893          12 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
    3894             : }
    3895             : 
    3896             : /************************************************************************/
    3897             : /*                           GDALMDArray()                              */
    3898             : /************************************************************************/
    3899             : 
    3900             : //! @cond Doxygen_Suppress
    3901        7379 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
    3902             :                          CPL_UNUSED const std::string &osName,
    3903           0 :                          const std::string &osContext)
    3904             :     :
    3905             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
    3906             :       GDALAbstractMDArray(osParentName, osName),
    3907             : #endif
    3908        7379 :       m_osContext(osContext)
    3909             : {
    3910        7379 : }
    3911             : 
    3912             : //! @endcond
    3913             : 
    3914             : /************************************************************************/
    3915             : /*                           GetTotalCopyCost()                         */
    3916             : /************************************************************************/
    3917             : 
    3918             : /** Return a total "cost" to copy the array.
    3919             :  *
    3920             :  * Used as a parameter for CopyFrom()
    3921             :  */
    3922          70 : GUInt64 GDALMDArray::GetTotalCopyCost() const
    3923             : {
    3924         140 :     return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
    3925         140 :            GetTotalElementsCount() * GetDataType().GetSize();
    3926             : }
    3927             : 
    3928             : /************************************************************************/
    3929             : /*                       CopyFromAllExceptValues()                      */
    3930             : /************************************************************************/
    3931             : 
    3932             : //! @cond Doxygen_Suppress
    3933             : 
    3934         209 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
    3935             :                                           bool bStrict, GUInt64 &nCurCost,
    3936             :                                           const GUInt64 nTotalCost,
    3937             :                                           GDALProgressFunc pfnProgress,
    3938             :                                           void *pProgressData)
    3939             : {
    3940             :     // Nodata setting must be one of the first things done for TileDB
    3941         209 :     const void *pNoData = poSrcArray->GetRawNoDataValue();
    3942         209 :     if (pNoData && poSrcArray->GetDataType() == GetDataType())
    3943             :     {
    3944          13 :         SetRawNoDataValue(pNoData);
    3945             :     }
    3946             : 
    3947         209 :     const bool bThisIsUnscaledArray =
    3948         209 :         dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
    3949         418 :     auto attrs = poSrcArray->GetAttributes();
    3950         282 :     for (const auto &attr : attrs)
    3951             :     {
    3952          73 :         const auto &osAttrName = attr->GetName();
    3953          73 :         if (bThisIsUnscaledArray)
    3954             :         {
    3955           6 :             if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
    3956           7 :                 osAttrName == "valid_min" || osAttrName == "valid_max" ||
    3957           1 :                 osAttrName == "valid_range")
    3958             :             {
    3959           1 :                 continue;
    3960             :             }
    3961             :         }
    3962             : 
    3963          72 :         auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
    3964         144 :                                        attr->GetDataType());
    3965          72 :         if (!dstAttr)
    3966             :         {
    3967           0 :             if (bStrict)
    3968           0 :                 return false;
    3969           0 :             continue;
    3970             :         }
    3971          72 :         auto raw = attr->ReadAsRaw();
    3972          72 :         if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
    3973           0 :             return false;
    3974             :     }
    3975         209 :     if (!attrs.empty())
    3976             :     {
    3977          42 :         nCurCost += attrs.size() * GDALAttribute::COPY_COST;
    3978          73 :         if (pfnProgress &&
    3979          31 :             !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    3980           0 :             return false;
    3981             :     }
    3982             : 
    3983         209 :     auto srcSRS = poSrcArray->GetSpatialRef();
    3984         209 :     if (srcSRS)
    3985             :     {
    3986          19 :         SetSpatialRef(srcSRS.get());
    3987             :     }
    3988             : 
    3989         209 :     const std::string &osUnit(poSrcArray->GetUnit());
    3990         209 :     if (!osUnit.empty())
    3991             :     {
    3992          22 :         SetUnit(osUnit);
    3993             :     }
    3994             : 
    3995         209 :     bool bGotValue = false;
    3996         209 :     GDALDataType eOffsetStorageType = GDT_Unknown;
    3997             :     const double dfOffset =
    3998         209 :         poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
    3999         209 :     if (bGotValue)
    4000             :     {
    4001           3 :         SetOffset(dfOffset, eOffsetStorageType);
    4002             :     }
    4003             : 
    4004         209 :     bGotValue = false;
    4005         209 :     GDALDataType eScaleStorageType = GDT_Unknown;
    4006         209 :     const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
    4007         209 :     if (bGotValue)
    4008             :     {
    4009           3 :         SetScale(dfScale, eScaleStorageType);
    4010             :     }
    4011             : 
    4012         209 :     return true;
    4013             : }
    4014             : 
    4015             : //! @endcond
    4016             : 
    4017             : /************************************************************************/
    4018             : /*                               CopyFrom()                             */
    4019             : /************************************************************************/
    4020             : 
    4021             : /** Copy the content of an array into a new (generally empty) array.
    4022             :  *
    4023             :  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
    4024             :  *                   of some output drivers this is not recommended)
    4025             :  * @param poSrcArray Source array. Should NOT be nullptr.
    4026             :  * @param bStrict Whether to enable strict mode. In strict mode, any error will
    4027             :  *                stop the copy. In relaxed mode, the copy will be attempted to
    4028             :  *                be pursued.
    4029             :  * @param nCurCost  Should be provided as a variable initially set to 0.
    4030             :  * @param nTotalCost Total cost from GetTotalCopyCost().
    4031             :  * @param pfnProgress Progress callback, or nullptr.
    4032             :  * @param pProgressData Progress user data, or nulptr.
    4033             :  *
    4034             :  * @return true in case of success (or partial success if bStrict == false).
    4035             :  */
    4036          68 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
    4037             :                            const GDALMDArray *poSrcArray, bool bStrict,
    4038             :                            GUInt64 &nCurCost, const GUInt64 nTotalCost,
    4039             :                            GDALProgressFunc pfnProgress, void *pProgressData)
    4040             : {
    4041          68 :     if (pfnProgress == nullptr)
    4042           4 :         pfnProgress = GDALDummyProgress;
    4043             : 
    4044          68 :     nCurCost += GDALMDArray::COPY_COST;
    4045             : 
    4046          68 :     if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
    4047             :                                  pfnProgress, pProgressData))
    4048             :     {
    4049           0 :         return false;
    4050             :     }
    4051             : 
    4052          68 :     const auto &dims = poSrcArray->GetDimensions();
    4053          68 :     const auto nDTSize = poSrcArray->GetDataType().GetSize();
    4054          68 :     if (dims.empty())
    4055             :     {
    4056           2 :         std::vector<GByte> abyTmp(nDTSize);
    4057           2 :         if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
    4058           2 :                                GetDataType(), &abyTmp[0]) &&
    4059           2 :               Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
    4060           4 :                     &abyTmp[0])) &&
    4061             :             bStrict)
    4062             :         {
    4063           0 :             return false;
    4064             :         }
    4065           2 :         nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
    4066           2 :         if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    4067           0 :             return false;
    4068             :     }
    4069             :     else
    4070             :     {
    4071          66 :         std::vector<GUInt64> arrayStartIdx(dims.size());
    4072          66 :         std::vector<GUInt64> count(dims.size());
    4073         172 :         for (size_t i = 0; i < dims.size(); i++)
    4074             :         {
    4075         106 :             count[i] = static_cast<size_t>(dims[i]->GetSize());
    4076             :         }
    4077             : 
    4078             :         struct CopyFunc
    4079             :         {
    4080             :             GDALMDArray *poDstArray = nullptr;
    4081             :             std::vector<GByte> abyTmp{};
    4082             :             GDALProgressFunc pfnProgress = nullptr;
    4083             :             void *pProgressData = nullptr;
    4084             :             GUInt64 nCurCost = 0;
    4085             :             GUInt64 nTotalCost = 0;
    4086             :             GUInt64 nTotalBytesThisArray = 0;
    4087             :             bool bStop = false;
    4088             : 
    4089          84 :             static bool f(GDALAbstractMDArray *l_poSrcArray,
    4090             :                           const GUInt64 *chunkArrayStartIdx,
    4091             :                           const size_t *chunkCount, GUInt64 iCurChunk,
    4092             :                           GUInt64 nChunkCount, void *pUserData)
    4093             :             {
    4094          84 :                 const auto &dt(l_poSrcArray->GetDataType());
    4095          84 :                 auto data = static_cast<CopyFunc *>(pUserData);
    4096          84 :                 auto poDstArray = data->poDstArray;
    4097          84 :                 if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
    4098          84 :                                         nullptr, dt, &data->abyTmp[0]))
    4099             :                 {
    4100           1 :                     return false;
    4101             :                 }
    4102             :                 bool bRet =
    4103          83 :                     poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
    4104          83 :                                       nullptr, dt, &data->abyTmp[0]);
    4105          83 :                 if (dt.NeedsFreeDynamicMemory())
    4106             :                 {
    4107           5 :                     const auto l_nDTSize = dt.GetSize();
    4108           5 :                     GByte *ptr = &data->abyTmp[0];
    4109           5 :                     const size_t l_nDims(l_poSrcArray->GetDimensionCount());
    4110           5 :                     size_t nEltCount = 1;
    4111          10 :                     for (size_t i = 0; i < l_nDims; ++i)
    4112             :                     {
    4113           5 :                         nEltCount *= chunkCount[i];
    4114             :                     }
    4115          22 :                     for (size_t i = 0; i < nEltCount; i++)
    4116             :                     {
    4117          17 :                         dt.FreeDynamicMemory(ptr);
    4118          17 :                         ptr += l_nDTSize;
    4119             :                     }
    4120             :                 }
    4121          83 :                 if (!bRet)
    4122             :                 {
    4123           0 :                     return false;
    4124             :                 }
    4125             : 
    4126          83 :                 double dfCurCost =
    4127          83 :                     double(data->nCurCost) + double(iCurChunk) / nChunkCount *
    4128          83 :                                                  data->nTotalBytesThisArray;
    4129          83 :                 if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
    4130             :                                        data->pProgressData))
    4131             :                 {
    4132           0 :                     data->bStop = true;
    4133           0 :                     return false;
    4134             :                 }
    4135             : 
    4136          83 :                 return true;
    4137             :             }
    4138             :         };
    4139             : 
    4140          66 :         CopyFunc copyFunc;
    4141          66 :         copyFunc.poDstArray = this;
    4142          66 :         copyFunc.nCurCost = nCurCost;
    4143          66 :         copyFunc.nTotalCost = nTotalCost;
    4144          66 :         copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
    4145          66 :         copyFunc.pfnProgress = pfnProgress;
    4146          66 :         copyFunc.pProgressData = pProgressData;
    4147             :         const char *pszSwathSize =
    4148          66 :             CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
    4149             :         const size_t nMaxChunkSize =
    4150             :             pszSwathSize
    4151          66 :                 ? static_cast<size_t>(
    4152           1 :                       std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    4153           1 :                                CPLAtoGIntBig(pszSwathSize)))
    4154             :                 : static_cast<size_t>(
    4155          65 :                       std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    4156          65 :                                GDALGetCacheMax64() / 4));
    4157          66 :         const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
    4158          66 :         size_t nRealChunkSize = nDTSize;
    4159         172 :         for (const auto &nChunkSize : anChunkSizes)
    4160             :         {
    4161         106 :             nRealChunkSize *= nChunkSize;
    4162             :         }
    4163             :         try
    4164             :         {
    4165          66 :             copyFunc.abyTmp.resize(nRealChunkSize);
    4166             :         }
    4167           0 :         catch (const std::exception &)
    4168             :         {
    4169           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    4170             :                      "Cannot allocate temporary buffer");
    4171           0 :             nCurCost += copyFunc.nTotalBytesThisArray;
    4172           0 :             return false;
    4173             :         }
    4174         197 :         if (copyFunc.nTotalBytesThisArray != 0 &&
    4175          65 :             !const_cast<GDALMDArray *>(poSrcArray)
    4176          65 :                  ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
    4177             :                                    anChunkSizes.data(), CopyFunc::f,
    4178         132 :                                    &copyFunc) &&
    4179           1 :             (bStrict || copyFunc.bStop))
    4180             :         {
    4181           0 :             nCurCost += copyFunc.nTotalBytesThisArray;
    4182           0 :             return false;
    4183             :         }
    4184          66 :         nCurCost += copyFunc.nTotalBytesThisArray;
    4185             :     }
    4186             : 
    4187          68 :     return true;
    4188             : }
    4189             : 
    4190             : /************************************************************************/
    4191             : /*                         GetStructuralInfo()                          */
    4192             : /************************************************************************/
    4193             : 
    4194             : /** Return structural information on the array.
    4195             :  *
    4196             :  * This may be the compression, etc..
    4197             :  *
    4198             :  * The return value should not be freed and is valid until GDALMDArray is
    4199             :  * released or this function called again.
    4200             :  *
    4201             :  * This is the same as the C function GDALMDArrayGetStructuralInfo().
    4202             :  */
    4203          95 : CSLConstList GDALMDArray::GetStructuralInfo() const
    4204             : {
    4205          95 :     return nullptr;
    4206             : }
    4207             : 
    4208             : /************************************************************************/
    4209             : /*                          AdviseRead()                                */
    4210             : /************************************************************************/
    4211             : 
    4212             : /** Advise driver of upcoming read requests.
    4213             :  *
    4214             :  * Some GDAL drivers operate more efficiently if they know in advance what
    4215             :  * set of upcoming read requests will be made.  The AdviseRead() method allows
    4216             :  * an application to notify the driver of the region of interest.
    4217             :  *
    4218             :  * Many drivers just ignore the AdviseRead() call, but it can dramatically
    4219             :  * accelerate access via some drivers. One such case is when reading through
    4220             :  * a DAP dataset with the netCDF driver (a in-memory cache array is then created
    4221             :  * with the region of interest defined by AdviseRead())
    4222             :  *
    4223             :  * This is the same as the C function GDALMDArrayAdviseRead().
    4224             :  *
    4225             :  * @param arrayStartIdx Values representing the starting index to read
    4226             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    4227             :  *                      Array of GetDimensionCount() values.
    4228             :  *                      Can be nullptr as a synonymous for [0 for i in
    4229             :  * range(GetDimensionCount() ]
    4230             :  *
    4231             :  * @param count         Values representing the number of values to extract in
    4232             :  *                      each dimension.
    4233             :  *                      Array of GetDimensionCount() values.
    4234             :  *                      Can be nullptr as a synonymous for
    4235             :  *                      [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
    4236             :  * range(GetDimensionCount() ]
    4237             :  *
    4238             :  * @param papszOptions Driver specific options, or nullptr. Consult driver
    4239             :  * documentation.
    4240             :  *
    4241             :  * @return true in case of success (ignoring the advice is a success)
    4242             :  *
    4243             :  * @since GDAL 3.2
    4244             :  */
    4245          25 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    4246             :                              CSLConstList papszOptions) const
    4247             : {
    4248          25 :     const auto nDimCount = GetDimensionCount();
    4249          25 :     if (nDimCount == 0)
    4250           2 :         return true;
    4251             : 
    4252          46 :     std::vector<GUInt64> tmp_arrayStartIdx;
    4253          23 :     if (arrayStartIdx == nullptr)
    4254             :     {
    4255           0 :         tmp_arrayStartIdx.resize(nDimCount);
    4256           0 :         arrayStartIdx = tmp_arrayStartIdx.data();
    4257             :     }
    4258             : 
    4259          46 :     std::vector<size_t> tmp_count;
    4260          23 :     if (count == nullptr)
    4261             :     {
    4262           0 :         tmp_count.resize(nDimCount);
    4263           0 :         const auto &dims = GetDimensions();
    4264           0 :         for (size_t i = 0; i < nDimCount; i++)
    4265             :         {
    4266           0 :             const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
    4267             : #if SIZEOF_VOIDP < 8
    4268             :             if (nSize != static_cast<size_t>(nSize))
    4269             :             {
    4270             :                 CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
    4271             :                 return false;
    4272             :             }
    4273             : #endif
    4274           0 :             tmp_count[i] = static_cast<size_t>(nSize);
    4275             :         }
    4276           0 :         count = tmp_count.data();
    4277             :     }
    4278             : 
    4279          46 :     std::vector<GInt64> tmp_arrayStep;
    4280          46 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    4281          23 :     const GInt64 *arrayStep = nullptr;
    4282          23 :     const GPtrDiff_t *bufferStride = nullptr;
    4283          23 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    4284          46 :                               GDALExtendedDataType::Create(GDT_Unknown),
    4285             :                               nullptr, nullptr, 0, tmp_arrayStep,
    4286             :                               tmp_bufferStride))
    4287             :     {
    4288           1 :         return false;
    4289             :     }
    4290             : 
    4291          22 :     return IAdviseRead(arrayStartIdx, count, papszOptions);
    4292             : }
    4293             : 
    4294             : /************************************************************************/
    4295             : /*                             IAdviseRead()                            */
    4296             : /************************************************************************/
    4297             : 
    4298             : //! @cond Doxygen_Suppress
    4299           3 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
    4300             :                               CSLConstList /* papszOptions*/) const
    4301             : {
    4302           3 :     return true;
    4303             : }
    4304             : 
    4305             : //! @endcond
    4306             : 
    4307             : /************************************************************************/
    4308             : /*                            MassageName()                             */
    4309             : /************************************************************************/
    4310             : 
    4311             : //! @cond Doxygen_Suppress
    4312          32 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
    4313             : {
    4314          32 :     std::string ret;
    4315         604 :     for (const char ch : inputName)
    4316             :     {
    4317         572 :         if (!isalnum(static_cast<unsigned char>(ch)))
    4318         138 :             ret += '_';
    4319             :         else
    4320         434 :             ret += ch;
    4321             :     }
    4322          32 :     return ret;
    4323             : }
    4324             : 
    4325             : //! @endcond
    4326             : 
    4327             : /************************************************************************/
    4328             : /*                         GetCacheRootGroup()                          */
    4329             : /************************************************************************/
    4330             : 
    4331             : //! @cond Doxygen_Suppress
    4332             : std::shared_ptr<GDALGroup>
    4333        1600 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
    4334             :                                std::string &osCacheFilenameOut) const
    4335             : {
    4336        1600 :     const auto &osFilename = GetFilename();
    4337        1600 :     if (osFilename.empty())
    4338             :     {
    4339           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4340             :                  "Cannot cache an array with an empty filename");
    4341           1 :         return nullptr;
    4342             :     }
    4343             : 
    4344        1599 :     osCacheFilenameOut = osFilename + ".gmac";
    4345        1599 :     if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
    4346             :     {
    4347           0 :         const auto nPosQuestionMark = osFilename.find('?');
    4348           0 :         if (nPosQuestionMark != std::string::npos)
    4349             :         {
    4350             :             osCacheFilenameOut =
    4351           0 :                 osFilename.substr(0, nPosQuestionMark)
    4352           0 :                     .append(".gmac")
    4353           0 :                     .append(osFilename.substr(nPosQuestionMark));
    4354             :         }
    4355             :     }
    4356        1599 :     const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
    4357        1599 :     if (pszProxy != nullptr)
    4358           7 :         osCacheFilenameOut = pszProxy;
    4359             : 
    4360        1599 :     std::unique_ptr<GDALDataset> poDS;
    4361             :     VSIStatBufL sStat;
    4362        1599 :     if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
    4363             :     {
    4364          28 :         poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
    4365             :                                      GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
    4366             :                                      nullptr, nullptr, nullptr));
    4367             :     }
    4368        1599 :     if (poDS)
    4369             :     {
    4370          28 :         CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
    4371          28 :         return poDS->GetRootGroup();
    4372             :     }
    4373             : 
    4374        1571 :     if (bCanCreate)
    4375             :     {
    4376           4 :         const char *pszDrvName = "netCDF";
    4377           4 :         GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
    4378           4 :         if (poDrv == nullptr)
    4379             :         {
    4380           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
    4381             :                      pszDrvName);
    4382           0 :             return nullptr;
    4383             :         }
    4384             :         {
    4385           8 :             CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
    4386           8 :             CPLErrorStateBackuper oErrorStateBackuper;
    4387           4 :             poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
    4388             :                                                      nullptr, nullptr));
    4389             :         }
    4390           4 :         if (!poDS)
    4391             :         {
    4392           1 :             pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
    4393           1 :             if (pszProxy)
    4394             :             {
    4395           1 :                 osCacheFilenameOut = pszProxy;
    4396           1 :                 poDS.reset(poDrv->CreateMultiDimensional(
    4397             :                     osCacheFilenameOut.c_str(), nullptr, nullptr));
    4398             :             }
    4399             :         }
    4400           4 :         if (poDS)
    4401             :         {
    4402           4 :             CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
    4403           4 :             return poDS->GetRootGroup();
    4404             :         }
    4405             :         else
    4406             :         {
    4407           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4408             :                      "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
    4409             :                      "configuration option to write the cache in "
    4410             :                      "another directory",
    4411             :                      osCacheFilenameOut.c_str());
    4412             :         }
    4413             :     }
    4414             : 
    4415        1567 :     return nullptr;
    4416             : }
    4417             : 
    4418             : //! @endcond
    4419             : 
    4420             : /************************************************************************/
    4421             : /*                              Cache()                                 */
    4422             : /************************************************************************/
    4423             : 
    4424             : /** Cache the content of the array into an auxiliary filename.
    4425             :  *
    4426             :  * The main purpose of this method is to be able to cache views that are
    4427             :  * expensive to compute, such as transposed arrays.
    4428             :  *
    4429             :  * The array will be stored in a file whose name is the one of
    4430             :  * GetFilename(), with an extra .gmac extension (stands for GDAL
    4431             :  * Multidimensional Array Cache). The cache is a netCDF dataset.
    4432             :  *
    4433             :  * If the .gmac file cannot be written next to the dataset, the
    4434             :  * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
    4435             :  * directory.
    4436             :  *
    4437             :  * The GDALMDArray::Read() method will automatically use the cache when it
    4438             :  * exists. There is no timestamp checks between the source array and the cached
    4439             :  * array. If the source arrays changes, the cache must be manually deleted.
    4440             :  *
    4441             :  * This is the same as the C function GDALMDArrayCache()
    4442             :  *
    4443             :  * @note Driver implementation: optionally implemented.
    4444             :  *
    4445             :  * @param papszOptions List of options, null terminated, or NULL. Currently
    4446             :  *                     the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
    4447             :  *                     to specify the block size of the cached array.
    4448             :  * @return true in case of success.
    4449             :  */
    4450           7 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
    4451             : {
    4452          14 :     std::string osCacheFilename;
    4453          14 :     auto poRG = GetCacheRootGroup(true, osCacheFilename);
    4454           7 :     if (!poRG)
    4455           1 :         return false;
    4456             : 
    4457          12 :     const std::string osCachedArrayName(MassageName(GetFullName()));
    4458           6 :     if (poRG->OpenMDArray(osCachedArrayName))
    4459             :     {
    4460           2 :         CPLError(CE_Failure, CPLE_NotSupported,
    4461             :                  "An array with same name %s already exists in %s",
    4462             :                  osCachedArrayName.c_str(), osCacheFilename.c_str());
    4463           2 :         return false;
    4464             :     }
    4465             : 
    4466           8 :     CPLStringList aosOptions;
    4467           4 :     aosOptions.SetNameValue("COMPRESS", "DEFLATE");
    4468           4 :     const auto &aoDims = GetDimensions();
    4469           8 :     std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
    4470           4 :     if (!aoDims.empty())
    4471             :     {
    4472             :         std::string osBlockSize(
    4473           4 :             CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
    4474           4 :         if (osBlockSize.empty())
    4475             :         {
    4476           6 :             const auto anBlockSize = GetBlockSize();
    4477           3 :             int idxDim = 0;
    4478          10 :             for (auto nBlockSize : anBlockSize)
    4479             :             {
    4480           7 :                 if (idxDim > 0)
    4481           4 :                     osBlockSize += ',';
    4482           7 :                 if (nBlockSize == 0)
    4483           7 :                     nBlockSize = 256;
    4484           7 :                 nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
    4485             :                 osBlockSize +=
    4486           7 :                     std::to_string(static_cast<uint64_t>(nBlockSize));
    4487           7 :                 idxDim++;
    4488             :             }
    4489             :         }
    4490           4 :         aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
    4491             : 
    4492           4 :         int idxDim = 0;
    4493          13 :         for (const auto &poDim : aoDims)
    4494             :         {
    4495           9 :             auto poNewDim = poRG->CreateDimension(
    4496          18 :                 osCachedArrayName + '_' + std::to_string(idxDim),
    4497          18 :                 poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
    4498           9 :             if (!poNewDim)
    4499           0 :                 return false;
    4500           9 :             aoNewDims.emplace_back(poNewDim);
    4501           9 :             idxDim++;
    4502             :         }
    4503             :     }
    4504             : 
    4505           4 :     auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
    4506           8 :                                              GetDataType(), aosOptions.List());
    4507           4 :     if (!poCachedArray)
    4508             :     {
    4509           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
    4510             :                  osCachedArrayName.c_str(), osCacheFilename.c_str());
    4511           0 :         return false;
    4512             :     }
    4513             : 
    4514           4 :     GUInt64 nCost = 0;
    4515           8 :     return poCachedArray->CopyFrom(nullptr, this,
    4516             :                                    false,  // strict
    4517           4 :                                    nCost, GetTotalCopyCost(), nullptr, nullptr);
    4518             : }
    4519             : 
    4520             : /************************************************************************/
    4521             : /*                               Read()                                 */
    4522             : /************************************************************************/
    4523             : 
    4524        4340 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
    4525             :                        const GInt64 *arrayStep,         // step in elements
    4526             :                        const GPtrDiff_t *bufferStride,  // stride in elements
    4527             :                        const GDALExtendedDataType &bufferDataType,
    4528             :                        void *pDstBuffer, const void *pDstBufferAllocStart,
    4529             :                        size_t nDstBufferAllocSize) const
    4530             : {
    4531        4340 :     if (!m_bHasTriedCachedArray)
    4532             :     {
    4533        2047 :         m_bHasTriedCachedArray = true;
    4534        2047 :         if (IsCacheable())
    4535             :         {
    4536        2047 :             const auto &osFilename = GetFilename();
    4537        3442 :             if (!osFilename.empty() &&
    4538        3442 :                 !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
    4539             :             {
    4540        2770 :                 std::string osCacheFilename;
    4541        2770 :                 auto poRG = GetCacheRootGroup(false, osCacheFilename);
    4542        1385 :                 if (poRG)
    4543             :                 {
    4544             :                     const std::string osCachedArrayName(
    4545          32 :                         MassageName(GetFullName()));
    4546          16 :                     m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
    4547          16 :                     if (m_poCachedArray)
    4548             :                     {
    4549           6 :                         const auto &dims = GetDimensions();
    4550             :                         const auto &cachedDims =
    4551           6 :                             m_poCachedArray->GetDimensions();
    4552           6 :                         const size_t nDims = dims.size();
    4553             :                         bool ok =
    4554          12 :                             m_poCachedArray->GetDataType() == GetDataType() &&
    4555           6 :                             cachedDims.size() == nDims;
    4556          19 :                         for (size_t i = 0; ok && i < nDims; ++i)
    4557             :                         {
    4558          13 :                             ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
    4559             :                         }
    4560           6 :                         if (ok)
    4561             :                         {
    4562           6 :                             CPLDebug("GDAL", "Cached array for %s found in %s",
    4563             :                                      osCachedArrayName.c_str(),
    4564             :                                      osCacheFilename.c_str());
    4565             :                         }
    4566             :                         else
    4567             :                         {
    4568           0 :                             CPLError(CE_Warning, CPLE_AppDefined,
    4569             :                                      "Cached array %s in %s has incompatible "
    4570             :                                      "characteristics with current array.",
    4571             :                                      osCachedArrayName.c_str(),
    4572             :                                      osCacheFilename.c_str());
    4573           0 :                             m_poCachedArray.reset();
    4574             :                         }
    4575             :                     }
    4576             :                 }
    4577             :             }
    4578             :         }
    4579             :     }
    4580             : 
    4581        4340 :     const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
    4582        4340 :     if (!array->GetDataType().CanConvertTo(bufferDataType))
    4583             :     {
    4584           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4585             :                  "Array data type is not convertible to buffer data type");
    4586           0 :         return false;
    4587             :     }
    4588             : 
    4589        8680 :     std::vector<GInt64> tmp_arrayStep;
    4590        8680 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    4591        4340 :     if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
    4592             :                                      bufferStride, bufferDataType, pDstBuffer,
    4593             :                                      pDstBufferAllocStart, nDstBufferAllocSize,
    4594             :                                      tmp_arrayStep, tmp_bufferStride))
    4595             :     {
    4596           9 :         return false;
    4597             :     }
    4598             : 
    4599        4331 :     return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
    4600        4331 :                         bufferDataType, pDstBuffer);
    4601             : }
    4602             : 
    4603             : /************************************************************************/
    4604             : /*                          GetRootGroup()                              */
    4605             : /************************************************************************/
    4606             : 
    4607             : /** Return the root group to which this arrays belongs too.
    4608             :  *
    4609             :  * Note that arrays may be free standing and some drivers may not implement
    4610             :  * this method, hence nullptr may be returned.
    4611             :  *
    4612             :  * It is used internally by the GetResampled() method to detect if GLT
    4613             :  * orthorectification is available.
    4614             :  *
    4615             :  * @return the root group, or nullptr.
    4616             :  * @since GDAL 3.8
    4617             :  */
    4618           0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
    4619             : {
    4620           0 :     return nullptr;
    4621             : }
    4622             : 
    4623             : //! @cond Doxygen_Suppress
    4624             : 
    4625             : /************************************************************************/
    4626             : /*                       IsTransposedRequest()                          */
    4627             : /************************************************************************/
    4628             : 
    4629         947 : bool GDALMDArray::IsTransposedRequest(
    4630             :     const size_t *count,
    4631             :     const GPtrDiff_t *bufferStride) const  // stride in elements
    4632             : {
    4633             :     /*
    4634             :     For example:
    4635             :     count = [2,3,4]
    4636             :     strides = [12, 4, 1]            (2-1)*12+(3-1)*4+(4-1)*1=23   ==> row major
    4637             :     stride [12, 1, 3]            (2-1)*12+(3-1)*1+(4-1)*3=23   ==>
    4638             :     (axis[0],axis[2],axis[1]) transposition [1, 8, 2]             (2-1)*1+
    4639             :     (3-1)*8+(4-1)*2=23   ==> (axis[2],axis[1],axis[0]) transposition
    4640             :     */
    4641         947 :     const size_t nDims(GetDimensionCount());
    4642         947 :     size_t nCurStrideForRowMajorStrides = 1;
    4643         947 :     bool bRowMajorStrides = true;
    4644         947 :     size_t nElts = 1;
    4645         947 :     size_t nLastIdx = 0;
    4646        2513 :     for (size_t i = nDims; i > 0;)
    4647             :     {
    4648        1566 :         --i;
    4649        1566 :         if (bufferStride[i] < 0)
    4650           0 :             return false;
    4651        1566 :         if (static_cast<size_t>(bufferStride[i]) !=
    4652             :             nCurStrideForRowMajorStrides)
    4653             :         {
    4654         278 :             bRowMajorStrides = false;
    4655             :         }
    4656             :         // Integer overflows have already been checked in CheckReadWriteParams()
    4657        1566 :         nCurStrideForRowMajorStrides *= count[i];
    4658        1566 :         nElts *= count[i];
    4659        1566 :         nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
    4660             :     }
    4661         947 :     if (bRowMajorStrides)
    4662         743 :         return false;
    4663         204 :     return nLastIdx == nElts - 1;
    4664             : }
    4665             : 
    4666             : /************************************************************************/
    4667             : /*                   CopyToFinalBufferSameDataType()                    */
    4668             : /************************************************************************/
    4669             : 
    4670             : template <size_t N>
    4671          72 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
    4672             :                                    size_t nDims, const size_t *count,
    4673             :                                    const GPtrDiff_t *bufferStride)
    4674             : {
    4675         144 :     std::vector<size_t> anStackCount(nDims);
    4676         144 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    4677          72 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
    4678             : #if defined(__GNUC__)
    4679             : #pragma GCC diagnostic push
    4680             : #pragma GCC diagnostic ignored "-Wnull-dereference"
    4681             : #endif
    4682          72 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    4683             : #if defined(__GNUC__)
    4684             : #pragma GCC diagnostic pop
    4685             : #endif
    4686          72 :     size_t iDim = 0;
    4687             : 
    4688         761 : lbl_next_depth:
    4689         761 :     if (iDim == nDims - 1)
    4690             :     {
    4691         673 :         size_t n = count[iDim];
    4692         673 :         GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
    4693         673 :         const auto bufferStrideLastDim = bufferStride[iDim] * N;
    4694       29210 :         while (n > 0)
    4695             :         {
    4696       28537 :             --n;
    4697       28537 :             memcpy(pabyDstBuffer, pabySrcBuffer, N);
    4698       28537 :             pabyDstBuffer += bufferStrideLastDim;
    4699       28537 :             pabySrcBuffer += N;
    4700             :         }
    4701             :     }
    4702             :     else
    4703             :     {
    4704          88 :         anStackCount[iDim] = count[iDim];
    4705             :         while (true)
    4706             :         {
    4707         689 :             ++iDim;
    4708         689 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    4709         689 :             goto lbl_next_depth;
    4710         689 :         lbl_return_to_caller_in_loop:
    4711         689 :             --iDim;
    4712         689 :             --anStackCount[iDim];
    4713         689 :             if (anStackCount[iDim] == 0)
    4714          88 :                 break;
    4715         601 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
    4716             :         }
    4717             :     }
    4718         761 :     if (iDim > 0)
    4719         689 :         goto lbl_return_to_caller_in_loop;
    4720          72 : }
    4721             : 
    4722             : /************************************************************************/
    4723             : /*                        CopyToFinalBuffer()                           */
    4724             : /************************************************************************/
    4725             : 
    4726         183 : static void CopyToFinalBuffer(const void *pSrcBuffer,
    4727             :                               const GDALExtendedDataType &eSrcDataType,
    4728             :                               void *pDstBuffer,
    4729             :                               const GDALExtendedDataType &eDstDataType,
    4730             :                               size_t nDims, const size_t *count,
    4731             :                               const GPtrDiff_t *bufferStride)
    4732             : {
    4733         183 :     const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
    4734             :     // Use specialized implementation for well-known data types when no
    4735             :     // type conversion is needed
    4736         183 :     if (eSrcDataType == eDstDataType)
    4737             :     {
    4738         122 :         if (nSrcDataTypeSize == 1)
    4739             :         {
    4740          41 :             CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
    4741             :                                              count, bufferStride);
    4742          72 :             return;
    4743             :         }
    4744          81 :         else if (nSrcDataTypeSize == 2)
    4745             :         {
    4746           1 :             CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
    4747             :                                              count, bufferStride);
    4748           1 :             return;
    4749             :         }
    4750          80 :         else if (nSrcDataTypeSize == 4)
    4751             :         {
    4752          22 :             CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
    4753             :                                              count, bufferStride);
    4754          22 :             return;
    4755             :         }
    4756          58 :         else if (nSrcDataTypeSize == 8)
    4757             :         {
    4758           8 :             CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
    4759             :                                              count, bufferStride);
    4760           8 :             return;
    4761             :         }
    4762             :     }
    4763             : 
    4764         111 :     const size_t nDstDataTypeSize(eDstDataType.GetSize());
    4765         222 :     std::vector<size_t> anStackCount(nDims);
    4766         222 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    4767         111 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
    4768         111 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    4769         111 :     size_t iDim = 0;
    4770             : 
    4771         380 : lbl_next_depth:
    4772         380 :     if (iDim == nDims - 1)
    4773             :     {
    4774         370 :         GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
    4775         370 :                                          pabyDstBufferStack[iDim], eDstDataType,
    4776         370 :                                          bufferStride[iDim], count[iDim]);
    4777         370 :         pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
    4778             :     }
    4779             :     else
    4780             :     {
    4781          10 :         anStackCount[iDim] = count[iDim];
    4782             :         while (true)
    4783             :         {
    4784         269 :             ++iDim;
    4785         269 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    4786         269 :             goto lbl_next_depth;
    4787         269 :         lbl_return_to_caller_in_loop:
    4788         269 :             --iDim;
    4789         269 :             --anStackCount[iDim];
    4790         269 :             if (anStackCount[iDim] == 0)
    4791          10 :                 break;
    4792         259 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
    4793             :         }
    4794             :     }
    4795         380 :     if (iDim > 0)
    4796         269 :         goto lbl_return_to_caller_in_loop;
    4797             : }
    4798             : 
    4799             : /************************************************************************/
    4800             : /*                      TransposeLast2Dims()                            */
    4801             : /************************************************************************/
    4802             : 
    4803          19 : static bool TransposeLast2Dims(void *pDstBuffer,
    4804             :                                const GDALExtendedDataType &eDT,
    4805             :                                const size_t nDims, const size_t *count,
    4806             :                                const size_t nEltsNonLast2Dims)
    4807             : {
    4808          19 :     const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
    4809          19 :     const auto nDTSize = eDT.GetSize();
    4810             :     void *pTempBufferForLast2DimsTranspose =
    4811          19 :         VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
    4812          19 :     if (pTempBufferForLast2DimsTranspose == nullptr)
    4813           0 :         return false;
    4814             : 
    4815          19 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
    4816          58 :     for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
    4817             :     {
    4818          39 :         GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
    4819             :                         pTempBufferForLast2DimsTranspose,
    4820          39 :                         eDT.GetNumericDataType(), count[nDims - 1],
    4821          39 :                         count[nDims - 2]);
    4822          39 :         memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
    4823             :                nDTSize * nEltsLast2Dims);
    4824          39 :         pabyDstBuffer += nDTSize * nEltsLast2Dims;
    4825             :     }
    4826             : 
    4827          19 :     VSIFree(pTempBufferForLast2DimsTranspose);
    4828             : 
    4829          19 :     return true;
    4830             : }
    4831             : 
    4832             : /************************************************************************/
    4833             : /*                      ReadForTransposedRequest()                      */
    4834             : /************************************************************************/
    4835             : 
    4836             : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
    4837             : // transposed view yield to extremely poor/unusable performance. This fixes
    4838             : // this by using temporary memory to read in a contiguous buffer in a
    4839             : // row-major order, and then do the transposition to the final buffer.
    4840             : 
    4841         202 : bool GDALMDArray::ReadForTransposedRequest(
    4842             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    4843             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
    4844             :     void *pDstBuffer) const
    4845             : {
    4846         202 :     const size_t nDims(GetDimensionCount());
    4847         202 :     if (nDims == 0)
    4848             :     {
    4849           0 :         CPLAssert(false);
    4850             :         return false;  // shouldn't happen
    4851             :     }
    4852         202 :     size_t nElts = 1;
    4853         526 :     for (size_t i = 0; i < nDims; ++i)
    4854         324 :         nElts *= count[i];
    4855             : 
    4856         404 :     std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
    4857         202 :     tmpBufferStrides.back() = 1;
    4858         324 :     for (size_t i = nDims - 1; i > 0;)
    4859             :     {
    4860         122 :         --i;
    4861         122 :         tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
    4862             :     }
    4863             : 
    4864         202 :     const auto &eDT = GetDataType();
    4865         202 :     const auto nDTSize = eDT.GetSize();
    4866         343 :     if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
    4867         359 :         static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
    4868          16 :         (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
    4869             :     {
    4870             :         // Optimization of the optimization if only the last 2 dims are
    4871             :         // transposed that saves on temporary buffer allocation
    4872          23 :         const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
    4873          23 :         size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
    4874          23 :         bool bRowMajorStridesForNonLast2Dims = true;
    4875          23 :         size_t nEltsNonLast2Dims = 1;
    4876          40 :         for (size_t i = nDims - 2; i > 0;)
    4877             :         {
    4878          17 :             --i;
    4879          17 :             if (static_cast<size_t>(bufferStride[i]) !=
    4880             :                 nCurStrideForRowMajorStrides)
    4881             :             {
    4882           4 :                 bRowMajorStridesForNonLast2Dims = false;
    4883             :             }
    4884             :             // Integer overflows have already been checked in
    4885             :             // CheckReadWriteParams()
    4886          17 :             nCurStrideForRowMajorStrides *= count[i];
    4887          17 :             nEltsNonLast2Dims *= count[i];
    4888             :         }
    4889          23 :         if (bRowMajorStridesForNonLast2Dims)
    4890             :         {
    4891             :             // We read in the final buffer!
    4892          19 :             if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
    4893          19 :                        eDT, pDstBuffer))
    4894             :             {
    4895           0 :                 return false;
    4896             :             }
    4897             : 
    4898          19 :             return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
    4899          19 :                                       nEltsNonLast2Dims);
    4900             :         }
    4901             :     }
    4902             : 
    4903         183 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
    4904         183 :     if (pTempBuffer == nullptr)
    4905           0 :         return false;
    4906             : 
    4907         183 :     if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
    4908         183 :                pTempBuffer))
    4909             :     {
    4910           0 :         VSIFree(pTempBuffer);
    4911           0 :         return false;
    4912             :     }
    4913         183 :     CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
    4914             :                       count, bufferStride);
    4915             : 
    4916         183 :     if (eDT.NeedsFreeDynamicMemory())
    4917             :     {
    4918         100 :         GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
    4919         200 :         for (size_t i = 0; i < nElts; ++i)
    4920             :         {
    4921         100 :             eDT.FreeDynamicMemory(pabyPtr);
    4922         100 :             pabyPtr += nDTSize;
    4923             :         }
    4924             :     }
    4925             : 
    4926         183 :     VSIFree(pTempBuffer);
    4927         183 :     return true;
    4928             : }
    4929             : 
    4930             : /************************************************************************/
    4931             : /*               IsStepOneContiguousRowMajorOrderedSameDataType()       */
    4932             : /************************************************************************/
    4933             : 
    4934             : // Returns true if at all following conditions are met:
    4935             : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
    4936             : // defines a row-major ordered contiguous buffer.
    4937          78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
    4938             :     const size_t *count, const GInt64 *arrayStep,
    4939             :     const GPtrDiff_t *bufferStride,
    4940             :     const GDALExtendedDataType &bufferDataType) const
    4941             : {
    4942          78 :     if (bufferDataType != GetDataType())
    4943           5 :         return false;
    4944          73 :     size_t nExpectedStride = 1;
    4945         166 :     for (size_t i = GetDimensionCount(); i > 0;)
    4946             :     {
    4947          96 :         --i;
    4948          96 :         if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
    4949          94 :             static_cast<size_t>(bufferStride[i]) != nExpectedStride)
    4950             :         {
    4951           3 :             return false;
    4952             :         }
    4953          93 :         nExpectedStride *= count[i];
    4954             :     }
    4955          70 :     return true;
    4956             : }
    4957             : 
    4958             : /************************************************************************/
    4959             : /*                      ReadUsingContiguousIRead()                      */
    4960             : /************************************************************************/
    4961             : 
    4962             : // Used for example by the TileDB driver when requesting it with
    4963             : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
    4964             : // not defining a row-major ordered contiguous buffer.
    4965             : // Should only be called when at least one of the above conditions are true,
    4966             : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
    4967             : // returning none.
    4968             : // This method will call IRead() again with arrayStep[] == 1,
    4969             : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
    4970             : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
    4971             : // content of that temporary buffer onto pDstBuffer.
    4972           7 : bool GDALMDArray::ReadUsingContiguousIRead(
    4973             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    4974             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
    4975             :     void *pDstBuffer) const
    4976             : {
    4977           7 :     const size_t nDims(GetDimensionCount());
    4978          14 :     std::vector<GUInt64> anTmpStartIdx(nDims);
    4979          14 :     std::vector<size_t> anTmpCount(nDims);
    4980           7 :     const auto &oType = GetDataType();
    4981           7 :     size_t nMemArraySize = oType.GetSize();
    4982          14 :     std::vector<GPtrDiff_t> anTmpStride(nDims);
    4983           7 :     GPtrDiff_t nStride = 1;
    4984          18 :     for (size_t i = nDims; i > 0;)
    4985             :     {
    4986          11 :         --i;
    4987          11 :         if (arrayStep[i] > 0)
    4988           9 :             anTmpStartIdx[i] = arrayStartIdx[i];
    4989             :         else
    4990           2 :             anTmpStartIdx[i] =
    4991           2 :                 arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
    4992             :         const uint64_t nCount =
    4993          11 :             (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
    4994          11 :         if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
    4995             :         {
    4996           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4997             :                      "Read() failed due to too large memory requirement");
    4998           0 :             return false;
    4999             :         }
    5000          11 :         anTmpCount[i] = static_cast<size_t>(nCount);
    5001          11 :         nMemArraySize *= anTmpCount[i];
    5002          11 :         anTmpStride[i] = nStride;
    5003          11 :         nStride *= anTmpCount[i];
    5004             :     }
    5005             :     std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
    5006          14 :         VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
    5007           7 :     if (!pTmpBuffer)
    5008           0 :         return false;
    5009           7 :     if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
    5010          14 :                std::vector<GInt64>(nDims, 1).data(),  // steps
    5011           7 :                anTmpStride.data(), oType, pTmpBuffer.get()))
    5012             :     {
    5013           0 :         return false;
    5014             :     }
    5015          14 :     std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
    5016          18 :     for (size_t i = 0; i < nDims; ++i)
    5017             :     {
    5018          11 :         if (arrayStep[i] > 0)
    5019           9 :             anTmpStartIdx[i] = 0;
    5020             :         else
    5021           2 :             anTmpStartIdx[i] = anTmpCount[i] - 1;
    5022          22 :         apoTmpDims[i] = std::make_shared<GDALDimension>(
    5023          22 :             std::string(), std::string(), std::string(), std::string(),
    5024          22 :             anTmpCount[i]);
    5025             :     }
    5026             :     auto poMEMArray =
    5027          14 :         MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
    5028          21 :     return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
    5029           7 :            poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
    5030           7 :                             bufferStride, bufferDataType, pDstBuffer);
    5031             : }
    5032             : 
    5033             : //! @endcond
    5034             : 
    5035             : /************************************************************************/
    5036             : /*                       GDALSlicedMDArray                              */
    5037             : /************************************************************************/
    5038             : 
    5039             : class GDALSlicedMDArray final : public GDALPamMDArray
    5040             : {
    5041             :   private:
    5042             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5043             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    5044             :     std::vector<size_t> m_mapDimIdxToParentDimIdx{};  // of size m_dims.size()
    5045             :     std::vector<std::shared_ptr<GDALMDArray>> m_apoNewIndexingVariables{};
    5046             :     std::vector<Range>
    5047             :         m_parentRanges{};  // of size m_poParent->GetDimensionCount()
    5048             : 
    5049             :     mutable std::vector<GUInt64> m_parentStart;
    5050             :     mutable std::vector<size_t> m_parentCount;
    5051             :     mutable std::vector<GInt64> m_parentStep;
    5052             :     mutable std::vector<GPtrDiff_t> m_parentStride;
    5053             : 
    5054             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
    5055             :                              const GInt64 *arrayStep,
    5056             :                              const GPtrDiff_t *bufferStride) const;
    5057             : 
    5058             :   protected:
    5059         686 :     explicit GDALSlicedMDArray(
    5060             :         const std::shared_ptr<GDALMDArray> &poParent,
    5061             :         const std::string &viewExpr,
    5062             :         std::vector<std::shared_ptr<GDALDimension>> &&dims,
    5063             :         std::vector<size_t> &&mapDimIdxToParentDimIdx,
    5064             :         std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
    5065             :         std::vector<Range> &&parentRanges)
    5066        2058 :         : GDALAbstractMDArray(std::string(), "Sliced view of " +
    5067        2058 :                                                  poParent->GetFullName() +
    5068        1372 :                                                  " (" + viewExpr + ")"),
    5069        1372 :           GDALPamMDArray(std::string(),
    5070        1372 :                          "Sliced view of " + poParent->GetFullName() + " (" +
    5071        1372 :                              viewExpr + ")",
    5072        1372 :                          GDALPamMultiDim::GetPAM(poParent),
    5073             :                          poParent->GetContext()),
    5074        1372 :           m_poParent(std::move(poParent)), m_dims(std::move(dims)),
    5075         686 :           m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
    5076         686 :           m_apoNewIndexingVariables(std::move(apoNewIndexingVariables)),
    5077         686 :           m_parentRanges(std::move(parentRanges)),
    5078         686 :           m_parentStart(m_poParent->GetDimensionCount()),
    5079         686 :           m_parentCount(m_poParent->GetDimensionCount(), 1),
    5080         686 :           m_parentStep(m_poParent->GetDimensionCount()),
    5081        5488 :           m_parentStride(m_poParent->GetDimensionCount())
    5082             :     {
    5083         686 :     }
    5084             : 
    5085             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5086             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5087             :                const GDALExtendedDataType &bufferDataType,
    5088             :                void *pDstBuffer) const override;
    5089             : 
    5090             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    5091             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5092             :                 const GDALExtendedDataType &bufferDataType,
    5093             :                 const void *pSrcBuffer) override;
    5094             : 
    5095             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5096             :                      CSLConstList papszOptions) const override;
    5097             : 
    5098             :   public:
    5099             :     static std::shared_ptr<GDALSlicedMDArray>
    5100         686 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5101             :            const std::string &viewExpr,
    5102             :            std::vector<std::shared_ptr<GDALDimension>> &&dims,
    5103             :            std::vector<size_t> &&mapDimIdxToParentDimIdx,
    5104             :            std::vector<std::shared_ptr<GDALMDArray>> &&apoNewIndexingVariables,
    5105             :            std::vector<Range> &&parentRanges)
    5106             :     {
    5107         686 :         CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
    5108         686 :         CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
    5109             : 
    5110             :         auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
    5111         686 :             poParent, viewExpr, std::move(dims),
    5112         686 :             std::move(mapDimIdxToParentDimIdx),
    5113         686 :             std::move(apoNewIndexingVariables), std::move(parentRanges))));
    5114         686 :         newAr->SetSelf(newAr);
    5115         686 :         return newAr;
    5116             :     }
    5117             : 
    5118          77 :     bool IsWritable() const override
    5119             :     {
    5120          77 :         return m_poParent->IsWritable();
    5121             :     }
    5122             : 
    5123        1119 :     const std::string &GetFilename() const override
    5124             :     {
    5125        1119 :         return m_poParent->GetFilename();
    5126             :     }
    5127             : 
    5128             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5129        4124 :     GetDimensions() const override
    5130             :     {
    5131        4124 :         return m_dims;
    5132             :     }
    5133             : 
    5134        1581 :     const GDALExtendedDataType &GetDataType() const override
    5135             :     {
    5136        1581 :         return m_poParent->GetDataType();
    5137             :     }
    5138             : 
    5139           2 :     const std::string &GetUnit() const override
    5140             :     {
    5141           2 :         return m_poParent->GetUnit();
    5142             :     }
    5143             : 
    5144             :     // bool SetUnit(const std::string& osUnit) override  { return
    5145             :     // m_poParent->SetUnit(osUnit); }
    5146             : 
    5147           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5148             :     {
    5149           4 :         auto poSrcSRS = m_poParent->GetSpatialRef();
    5150           2 :         if (!poSrcSRS)
    5151           1 :             return nullptr;
    5152           2 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
    5153           2 :         std::vector<int> dstMapping;
    5154           3 :         for (int srcAxis : srcMapping)
    5155             :         {
    5156           2 :             bool bFound = false;
    5157           3 :             for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
    5158             :             {
    5159           3 :                 if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
    5160           3 :                     srcAxis - 1)
    5161             :                 {
    5162           2 :                     dstMapping.push_back(static_cast<int>(i) + 1);
    5163           2 :                     bFound = true;
    5164           2 :                     break;
    5165             :                 }
    5166             :             }
    5167           2 :             if (!bFound)
    5168             :             {
    5169           0 :                 dstMapping.push_back(0);
    5170             :             }
    5171             :         }
    5172           2 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
    5173           1 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
    5174           1 :         return poClone;
    5175             :     }
    5176             : 
    5177          61 :     const void *GetRawNoDataValue() const override
    5178             :     {
    5179          61 :         return m_poParent->GetRawNoDataValue();
    5180             :     }
    5181             : 
    5182             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
    5183             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
    5184             : 
    5185           2 :     double GetOffset(bool *pbHasOffset,
    5186             :                      GDALDataType *peStorageType) const override
    5187             :     {
    5188           2 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5189             :     }
    5190             : 
    5191           2 :     double GetScale(bool *pbHasScale,
    5192             :                     GDALDataType *peStorageType) const override
    5193             :     {
    5194           2 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5195             :     }
    5196             : 
    5197             :     // bool SetOffset(double dfOffset) override { return
    5198             :     // m_poParent->SetOffset(dfOffset); }
    5199             : 
    5200             :     // bool SetScale(double dfScale) override { return
    5201             :     // m_poParent->SetScale(dfScale); }
    5202             : 
    5203         263 :     std::vector<GUInt64> GetBlockSize() const override
    5204             :     {
    5205         263 :         std::vector<GUInt64> ret(GetDimensionCount());
    5206         526 :         const auto parentBlockSize(m_poParent->GetBlockSize());
    5207         749 :         for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
    5208             :         {
    5209         486 :             const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
    5210         486 :             if (iOldAxis != static_cast<size_t>(-1))
    5211             :             {
    5212         486 :                 ret[i] = parentBlockSize[iOldAxis];
    5213             :             }
    5214             :         }
    5215         526 :         return ret;
    5216             :     }
    5217             : 
    5218             :     std::shared_ptr<GDALAttribute>
    5219           6 :     GetAttribute(const std::string &osName) const override
    5220             :     {
    5221           6 :         return m_poParent->GetAttribute(osName);
    5222             :     }
    5223             : 
    5224             :     std::vector<std::shared_ptr<GDALAttribute>>
    5225          35 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    5226             :     {
    5227          35 :         return m_poParent->GetAttributes(papszOptions);
    5228             :     }
    5229             : };
    5230             : 
    5231             : /************************************************************************/
    5232             : /*                        PrepareParentArrays()                         */
    5233             : /************************************************************************/
    5234             : 
    5235         549 : void GDALSlicedMDArray::PrepareParentArrays(
    5236             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    5237             :     const GPtrDiff_t *bufferStride) const
    5238             : {
    5239         549 :     const size_t nParentDimCount = m_parentRanges.size();
    5240        1655 :     for (size_t i = 0; i < nParentDimCount; i++)
    5241             :     {
    5242             :         // For dimensions in parent that have no existence in sliced array
    5243        1106 :         m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
    5244             :     }
    5245             : 
    5246        1424 :     for (size_t i = 0; i < m_dims.size(); i++)
    5247             :     {
    5248         875 :         const auto iParent = m_mapDimIdxToParentDimIdx[i];
    5249         875 :         if (iParent != static_cast<size_t>(-1))
    5250             :         {
    5251         873 :             m_parentStart[iParent] =
    5252         873 :                 m_parentRanges[iParent].m_nIncr >= 0
    5253         873 :                     ? m_parentRanges[iParent].m_nStartIdx +
    5254         785 :                           arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
    5255          88 :                     : m_parentRanges[iParent].m_nStartIdx -
    5256         176 :                           arrayStartIdx[i] *
    5257          88 :                               static_cast<GUInt64>(
    5258          88 :                                   -m_parentRanges[iParent].m_nIncr);
    5259         873 :             m_parentCount[iParent] = count[i];
    5260         873 :             if (arrayStep)
    5261             :             {
    5262         872 :                 m_parentStep[iParent] =
    5263         872 :                     count[i] == 1 ? 1 :
    5264             :                                   // other checks should have ensured this does
    5265             :                         // not overflow
    5266         681 :                         arrayStep[i] * m_parentRanges[iParent].m_nIncr;
    5267             :             }
    5268         873 :             if (bufferStride)
    5269             :             {
    5270         872 :                 m_parentStride[iParent] = bufferStride[i];
    5271             :             }
    5272             :         }
    5273             :     }
    5274         549 : }
    5275             : 
    5276             : /************************************************************************/
    5277             : /*                             IRead()                                  */
    5278             : /************************************************************************/
    5279             : 
    5280         516 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5281             :                               const GInt64 *arrayStep,
    5282             :                               const GPtrDiff_t *bufferStride,
    5283             :                               const GDALExtendedDataType &bufferDataType,
    5284             :                               void *pDstBuffer) const
    5285             : {
    5286         516 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5287        1032 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
    5288         516 :                             m_parentStep.data(), m_parentStride.data(),
    5289         516 :                             bufferDataType, pDstBuffer);
    5290             : }
    5291             : 
    5292             : /************************************************************************/
    5293             : /*                             IWrite()                                  */
    5294             : /************************************************************************/
    5295             : 
    5296          32 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
    5297             :                                const size_t *count, const GInt64 *arrayStep,
    5298             :                                const GPtrDiff_t *bufferStride,
    5299             :                                const GDALExtendedDataType &bufferDataType,
    5300             :                                const void *pSrcBuffer)
    5301             : {
    5302          32 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5303          64 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
    5304          32 :                              m_parentStep.data(), m_parentStride.data(),
    5305          32 :                              bufferDataType, pSrcBuffer);
    5306             : }
    5307             : 
    5308             : /************************************************************************/
    5309             : /*                             IAdviseRead()                            */
    5310             : /************************************************************************/
    5311             : 
    5312           1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
    5313             :                                     const size_t *count,
    5314             :                                     CSLConstList papszOptions) const
    5315             : {
    5316           1 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
    5317           1 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
    5318           1 :                                   papszOptions);
    5319             : }
    5320             : 
    5321             : /************************************************************************/
    5322             : /*                        CreateSlicedArray()                           */
    5323             : /************************************************************************/
    5324             : 
    5325             : static std::shared_ptr<GDALMDArray>
    5326         621 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
    5327             :                   const std::string &viewExpr, const std::string &activeSlice,
    5328             :                   bool bRenameDimensions,
    5329             :                   std::vector<GDALMDArray::ViewSpec> &viewSpecs)
    5330             : {
    5331         621 :     const auto &srcDims(self->GetDimensions());
    5332         621 :     if (srcDims.empty())
    5333             :     {
    5334           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
    5335           2 :         return nullptr;
    5336             :     }
    5337             : 
    5338        1238 :     CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
    5339         619 :     const auto nTokens = static_cast<size_t>(aosTokens.size());
    5340             : 
    5341        1238 :     std::vector<std::shared_ptr<GDALDimension>> newDims;
    5342        1238 :     std::vector<size_t> mapDimIdxToParentDimIdx;
    5343        1238 :     std::vector<GDALSlicedMDArray::Range> parentRanges;
    5344         619 :     newDims.reserve(nTokens);
    5345         619 :     mapDimIdxToParentDimIdx.reserve(nTokens);
    5346         619 :     parentRanges.reserve(nTokens);
    5347             : 
    5348         619 :     bool bGotEllipsis = false;
    5349         619 :     size_t nCurSrcDim = 0;
    5350        1238 :     std::vector<std::shared_ptr<GDALMDArray>> apoNewIndexingVariables;
    5351        1833 :     for (size_t i = 0; i < nTokens; i++)
    5352             :     {
    5353        1230 :         const char *pszIdxSpec = aosTokens[i];
    5354        1230 :         if (EQUAL(pszIdxSpec, "..."))
    5355             :         {
    5356          46 :             if (bGotEllipsis)
    5357             :             {
    5358           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5359             :                          "Only one single ellipsis is supported");
    5360           2 :                 return nullptr;
    5361             :             }
    5362          44 :             bGotEllipsis = true;
    5363          44 :             const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
    5364          97 :             for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
    5365             :             {
    5366          53 :                 parentRanges.emplace_back(0, 1);
    5367          53 :                 newDims.push_back(srcDims[nCurSrcDim]);
    5368          53 :                 mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5369             :             }
    5370          44 :             continue;
    5371             :         }
    5372        1184 :         else if (EQUAL(pszIdxSpec, "newaxis") ||
    5373        1181 :                  EQUAL(pszIdxSpec, "np.newaxis"))
    5374             :         {
    5375           3 :             newDims.push_back(std::make_shared<GDALDimension>(
    5376           6 :                 std::string(), "newaxis", std::string(), std::string(), 1));
    5377           3 :             mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
    5378           3 :             continue;
    5379             :         }
    5380        1181 :         else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
    5381             :         {
    5382         326 :             if (nCurSrcDim >= srcDims.size())
    5383             :             {
    5384           2 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
    5385             :                          activeSlice.c_str());
    5386           7 :                 return nullptr;
    5387             :             }
    5388             : 
    5389         324 :             auto nVal = CPLAtoGIntBig(pszIdxSpec);
    5390         324 :             GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
    5391         324 :             if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
    5392         320 :                 (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
    5393             :             {
    5394           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5395             :                          "Index " CPL_FRMT_GIB " is out of bounds", nVal);
    5396           5 :                 return nullptr;
    5397             :             }
    5398         319 :             if (nVal < 0)
    5399           0 :                 nVal += nDimSize;
    5400         319 :             parentRanges.emplace_back(nVal, 0);
    5401             :         }
    5402             :         else
    5403             :         {
    5404         855 :             if (nCurSrcDim >= srcDims.size())
    5405             :             {
    5406           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
    5407             :                          activeSlice.c_str());
    5408           7 :                 return nullptr;
    5409             :             }
    5410             : 
    5411             :             CPLStringList aosRangeTokens(
    5412         854 :                 CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
    5413         854 :             int nRangeTokens = aosRangeTokens.size();
    5414         854 :             if (nRangeTokens > 3)
    5415             :             {
    5416           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
    5417             :                          pszIdxSpec);
    5418           1 :                 return nullptr;
    5419             :             }
    5420         853 :             if (nRangeTokens <= 1)
    5421             :             {
    5422           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
    5423             :                          pszIdxSpec);
    5424           1 :                 return nullptr;
    5425             :             }
    5426         852 :             const char *pszStart = aosRangeTokens[0];
    5427         852 :             const char *pszEnd = aosRangeTokens[1];
    5428         852 :             const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
    5429         852 :             GDALSlicedMDArray::Range range;
    5430         852 :             const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
    5431         852 :             range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
    5432        1703 :             if (range.m_nIncr == 0 ||
    5433         851 :                 range.m_nIncr == std::numeric_limits<GInt64>::min())
    5434             :             {
    5435           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
    5436           1 :                 return nullptr;
    5437             :             }
    5438         851 :             auto startIdx(CPLAtoGIntBig(pszStart));
    5439         851 :             if (startIdx < 0)
    5440             :             {
    5441           0 :                 if (nDimSize < static_cast<GUInt64>(-startIdx))
    5442           0 :                     startIdx = 0;
    5443             :                 else
    5444           0 :                     startIdx = nDimSize + startIdx;
    5445             :             }
    5446         851 :             const bool bPosIncr = range.m_nIncr > 0;
    5447         851 :             range.m_nStartIdx = startIdx;
    5448        1702 :             range.m_nStartIdx = EQUAL(pszStart, "")
    5449         851 :                                     ? (bPosIncr ? 0 : nDimSize - 1)
    5450             :                                     : range.m_nStartIdx;
    5451         851 :             if (range.m_nStartIdx >= nDimSize - 1)
    5452         199 :                 range.m_nStartIdx = nDimSize - 1;
    5453         851 :             auto endIdx(CPLAtoGIntBig(pszEnd));
    5454         851 :             if (endIdx < 0)
    5455             :             {
    5456           1 :                 const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
    5457           1 :                 if (nDimSize < positiveEndIdx)
    5458           0 :                     endIdx = 0;
    5459             :                 else
    5460           1 :                     endIdx = nDimSize - positiveEndIdx;
    5461             :             }
    5462         851 :             GUInt64 nEndIdx = endIdx;
    5463         851 :             nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
    5464         851 :             if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
    5465         849 :                 (!bPosIncr && range.m_nStartIdx <= nEndIdx))
    5466             :             {
    5467           3 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5468             :                          "Output dimension of size 0 is not allowed");
    5469           3 :                 return nullptr;
    5470             :             }
    5471         848 :             int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
    5472         848 :             const auto nAbsIncr = std::abs(range.m_nIncr);
    5473         848 :             const GUInt64 newSize =
    5474             :                 bPosIncr
    5475         893 :                     ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
    5476          45 :                     : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
    5477         848 :             const auto &poSrcDim = srcDims[nCurSrcDim];
    5478        1396 :             if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
    5479         548 :                 newSize == poSrcDim->GetSize())
    5480             :             {
    5481         179 :                 newDims.push_back(poSrcDim);
    5482             :             }
    5483             :             else
    5484             :             {
    5485        1338 :                 std::string osNewDimName(poSrcDim->GetName());
    5486         669 :                 if (bRenameDimensions)
    5487             :                 {
    5488             :                     osNewDimName =
    5489        1242 :                         "subset_" + poSrcDim->GetName() +
    5490             :                         CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
    5491             :                                    "_" CPL_FRMT_GUIB,
    5492         621 :                                    static_cast<GUIntBig>(range.m_nStartIdx),
    5493         621 :                                    static_cast<GIntBig>(range.m_nIncr),
    5494         621 :                                    static_cast<GUIntBig>(newSize));
    5495             :                 }
    5496             :                 auto poNewDim = std::make_shared<GDALDimensionWeakIndexingVar>(
    5497        1338 :                     std::string(), osNewDimName, poSrcDim->GetType(),
    5498         669 :                     range.m_nIncr > 0 ? poSrcDim->GetDirection()
    5499             :                                       : std::string(),
    5500        1338 :                     newSize);
    5501         669 :                 auto poSrcIndexingVar = poSrcDim->GetIndexingVariable();
    5502         752 :                 if (poSrcIndexingVar &&
    5503         752 :                     poSrcIndexingVar->GetDimensionCount() == 1 &&
    5504          83 :                     poSrcIndexingVar->GetDimensions()[0] == poSrcDim)
    5505             :                 {
    5506             :                     std::vector<std::shared_ptr<GDALDimension>>
    5507         332 :                         indexingVarNewDims{poNewDim};
    5508         166 :                     std::vector<size_t> indexingVarMapDimIdxToParentDimIdx{0};
    5509             :                     std::vector<std::shared_ptr<GDALMDArray>>
    5510         166 :                         indexingVarNewIndexingVar;
    5511             :                     std::vector<GDALSlicedMDArray::Range>
    5512         166 :                         indexingVarParentRanges{range};
    5513             :                     auto poNewIndexingVar = GDALSlicedMDArray::Create(
    5514             :                         poSrcIndexingVar, pszIdxSpec,
    5515          83 :                         std::move(indexingVarNewDims),
    5516          83 :                         std::move(indexingVarMapDimIdxToParentDimIdx),
    5517          83 :                         std::move(indexingVarNewIndexingVar),
    5518         249 :                         std::move(indexingVarParentRanges));
    5519          83 :                     poNewDim->SetIndexingVariable(poNewIndexingVar);
    5520          83 :                     apoNewIndexingVariables.push_back(
    5521          83 :                         std::move(poNewIndexingVar));
    5522             :                 }
    5523         669 :                 newDims.push_back(std::move(poNewDim));
    5524             :             }
    5525         848 :             mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5526         848 :             parentRanges.emplace_back(range);
    5527             :         }
    5528             : 
    5529        1167 :         nCurSrcDim++;
    5530             :     }
    5531         676 :     for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
    5532             :     {
    5533          73 :         parentRanges.emplace_back(0, 1);
    5534          73 :         newDims.push_back(srcDims[nCurSrcDim]);
    5535          73 :         mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5536             :     }
    5537             : 
    5538         603 :     GDALMDArray::ViewSpec viewSpec;
    5539         603 :     viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
    5540         603 :     viewSpec.m_parentRanges = parentRanges;
    5541         603 :     viewSpecs.emplace_back(std::move(viewSpec));
    5542             : 
    5543        1206 :     return GDALSlicedMDArray::Create(
    5544         603 :         self, viewExpr, std::move(newDims), std::move(mapDimIdxToParentDimIdx),
    5545        1206 :         std::move(apoNewIndexingVariables), std::move(parentRanges));
    5546             : }
    5547             : 
    5548             : /************************************************************************/
    5549             : /*                       GDALExtractFieldMDArray                        */
    5550             : /************************************************************************/
    5551             : 
    5552             : class GDALExtractFieldMDArray final : public GDALPamMDArray
    5553             : {
    5554             :   private:
    5555             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5556             :     GDALExtendedDataType m_dt;
    5557             :     std::string m_srcCompName;
    5558             :     mutable std::vector<GByte> m_pabyNoData{};
    5559             : 
    5560             :   protected:
    5561          98 :     GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
    5562             :                             const std::string &fieldName,
    5563             :                             const std::unique_ptr<GDALEDTComponent> &srcComp)
    5564         392 :         : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
    5565         196 :                                                  " of " +
    5566          98 :                                                  poParent->GetFullName()),
    5567             :           GDALPamMDArray(
    5568         196 :               std::string(),
    5569         196 :               "Extract field " + fieldName + " of " + poParent->GetFullName(),
    5570         196 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
    5571             :           m_poParent(poParent), m_dt(srcComp->GetType()),
    5572         490 :           m_srcCompName(srcComp->GetName())
    5573             :     {
    5574          98 :         m_pabyNoData.resize(m_dt.GetSize());
    5575          98 :     }
    5576             : 
    5577             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5578             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5579             :                const GDALExtendedDataType &bufferDataType,
    5580             :                void *pDstBuffer) const override;
    5581             : 
    5582           1 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5583             :                      CSLConstList papszOptions) const override
    5584             :     {
    5585           1 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
    5586             :     }
    5587             : 
    5588             :   public:
    5589             :     static std::shared_ptr<GDALExtractFieldMDArray>
    5590          98 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5591             :            const std::string &fieldName,
    5592             :            const std::unique_ptr<GDALEDTComponent> &srcComp)
    5593             :     {
    5594             :         auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
    5595          98 :             new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
    5596          98 :         newAr->SetSelf(newAr);
    5597          98 :         return newAr;
    5598             :     }
    5599             : 
    5600         196 :     ~GDALExtractFieldMDArray() override
    5601          98 :     {
    5602          98 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
    5603         196 :     }
    5604             : 
    5605          51 :     bool IsWritable() const override
    5606             :     {
    5607          51 :         return m_poParent->IsWritable();
    5608             :     }
    5609             : 
    5610         309 :     const std::string &GetFilename() const override
    5611             :     {
    5612         309 :         return m_poParent->GetFilename();
    5613             :     }
    5614             : 
    5615             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5616         418 :     GetDimensions() const override
    5617             :     {
    5618         418 :         return m_poParent->GetDimensions();
    5619             :     }
    5620             : 
    5621         344 :     const GDALExtendedDataType &GetDataType() const override
    5622             :     {
    5623         344 :         return m_dt;
    5624             :     }
    5625             : 
    5626           2 :     const std::string &GetUnit() const override
    5627             :     {
    5628           2 :         return m_poParent->GetUnit();
    5629             :     }
    5630             : 
    5631           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5632             :     {
    5633           2 :         return m_poParent->GetSpatialRef();
    5634             :     }
    5635             : 
    5636          62 :     const void *GetRawNoDataValue() const override
    5637             :     {
    5638          62 :         const void *parentNoData = m_poParent->GetRawNoDataValue();
    5639          62 :         if (parentNoData == nullptr)
    5640           2 :             return nullptr;
    5641             : 
    5642          60 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
    5643          60 :         memset(&m_pabyNoData[0], 0, m_dt.GetSize());
    5644             : 
    5645         120 :         std::vector<std::unique_ptr<GDALEDTComponent>> comps;
    5646         120 :         comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
    5647         120 :             new GDALEDTComponent(m_srcCompName, 0, m_dt)));
    5648          60 :         auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
    5649         180 :                                                 std::move(comps)));
    5650             : 
    5651          60 :         GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
    5652          60 :                                         &m_pabyNoData[0], tmpDT);
    5653             : 
    5654          60 :         return &m_pabyNoData[0];
    5655             :     }
    5656             : 
    5657           2 :     double GetOffset(bool *pbHasOffset,
    5658             :                      GDALDataType *peStorageType) const override
    5659             :     {
    5660           2 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5661             :     }
    5662             : 
    5663           2 :     double GetScale(bool *pbHasScale,
    5664             :                     GDALDataType *peStorageType) const override
    5665             :     {
    5666           2 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5667             :     }
    5668             : 
    5669          52 :     std::vector<GUInt64> GetBlockSize() const override
    5670             :     {
    5671          52 :         return m_poParent->GetBlockSize();
    5672             :     }
    5673             : };
    5674             : 
    5675             : /************************************************************************/
    5676             : /*                             IRead()                                  */
    5677             : /************************************************************************/
    5678             : 
    5679         105 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
    5680             :                                     const size_t *count,
    5681             :                                     const GInt64 *arrayStep,
    5682             :                                     const GPtrDiff_t *bufferStride,
    5683             :                                     const GDALExtendedDataType &bufferDataType,
    5684             :                                     void *pDstBuffer) const
    5685             : {
    5686         210 :     std::vector<std::unique_ptr<GDALEDTComponent>> comps;
    5687         210 :     comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
    5688         210 :         new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
    5689             :     auto tmpDT(GDALExtendedDataType::Create(
    5690         210 :         std::string(), bufferDataType.GetSize(), std::move(comps)));
    5691             : 
    5692         105 :     return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
    5693         210 :                             tmpDT, pDstBuffer);
    5694             : }
    5695             : 
    5696             : /************************************************************************/
    5697             : /*                      CreateFieldNameExtractArray()                   */
    5698             : /************************************************************************/
    5699             : 
    5700             : static std::shared_ptr<GDALMDArray>
    5701          99 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
    5702             :                             const std::string &fieldName)
    5703             : {
    5704          99 :     CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
    5705          99 :     const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
    5706         235 :     for (const auto &comp : self->GetDataType().GetComponents())
    5707             :     {
    5708         234 :         if (comp->GetName() == fieldName)
    5709             :         {
    5710          98 :             srcComp = &comp;
    5711          98 :             break;
    5712             :         }
    5713             :     }
    5714          99 :     if (srcComp == nullptr)
    5715             :     {
    5716           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
    5717             :                  fieldName.c_str());
    5718           1 :         return nullptr;
    5719             :     }
    5720          98 :     return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
    5721             : }
    5722             : 
    5723             : /************************************************************************/
    5724             : /*                             GetView()                                */
    5725             : /************************************************************************/
    5726             : 
    5727             : // clang-format off
    5728             : /** Return a view of the array using slicing or field access.
    5729             :  *
    5730             :  * The slice expression uses the same syntax as NumPy basic slicing and
    5731             :  * indexing. See
    5732             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
    5733             :  * Or it can use field access by name. See
    5734             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
    5735             :  *
    5736             :  * Multiple [] bracket elements can be concatenated, with a slice expression
    5737             :  * or field name inside each.
    5738             :  *
    5739             :  * For basic slicing and indexing, inside each [] bracket element, a list of
    5740             :  * indexes that apply to successive source dimensions, can be specified, using
    5741             :  * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
    5742             :  * or newaxis, using a comma separator.
    5743             :  *
    5744             :  * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
    5745             :  * <ul>
    5746             :  * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
    5747             :  *     at index 1 in the first dimension, and index 2 in the second dimension
    5748             :  *     from the source array. That is 5</li>
    5749             :  * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
    5750             :  * implemented internally doing this intermediate slicing approach.</li>
    5751             :  * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
    5752             :  * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
    5753             :  *     first dimension. That is [4,5,6,7].</li>
    5754             :  * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
    5755             :  *     second dimension. That is [2,6].</li>
    5756             :  * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
    5757             :  * the second dimension. That is [[2],[6]].</li>
    5758             :  * <li>GetView("[::,2]"): Same as
    5759             :  * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
    5760             :  * ellipsis only expands to one dimension here.</li>
    5761             :  * <li>GetView("[:,::2]"):
    5762             :  * returns a 2-dimensional array, with even-indexed elements of the second
    5763             :  * dimension. That is [[0,2],[4,6]].</li>
    5764             :  * <li>GetView("[:,1::2]"): returns a
    5765             :  * 2-dimensional array, with odd-indexed elements of the second dimension. That
    5766             :  * is [[1,3],[5,7]].</li>
    5767             :  * <li>GetView("[:,1:3:]"): returns a 2-dimensional
    5768             :  * array, with elements of the second dimension with index in the range [1,3[.
    5769             :  * That is [[1,2],[5,6]].</li>
    5770             :  * <li>GetView("[::-1,:]"): returns a 2-dimensional
    5771             :  * array, with the values in first dimension reversed. That is
    5772             :  * [[4,5,6,7],[0,1,2,3]].</li>
    5773             :  * <li>GetView("[newaxis,...]"): returns a
    5774             :  * 3-dimensional array, with an additional dimension of size 1 put at the
    5775             :  * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
    5776             :  * </ul>
    5777             :  *
    5778             :  * One difference with NumPy behavior is that ranges that would result in
    5779             :  * zero elements are not allowed (dimensions of size 0 not being allowed in the
    5780             :  * GDAL multidimensional model).
    5781             :  *
    5782             :  * For field access, the syntax to use is ["field_name"] or ['field_name'].
    5783             :  * Multiple field specification is not supported currently.
    5784             :  *
    5785             :  * Both type of access can be combined, e.g. GetView("[1]['field_name']")
    5786             :  *
    5787             :  * \note When using the GDAL Python bindings, natural Python syntax can be
    5788             :  * used. That is ar[0,::,1]["foo"] will be internally translated to
    5789             :  * ar.GetView("[0,::,1]['foo']")
    5790             :  * \note When using the C++ API and integer indexing only, you may use the
    5791             :  * at(idx0, idx1, ...) method.
    5792             :  *
    5793             :  * The returned array holds a reference to the original one, and thus is
    5794             :  * a view of it (not a copy). If the content of the original array changes,
    5795             :  * the content of the view array too. When using basic slicing and indexing,
    5796             :  * the view can be written if the underlying array is writable.
    5797             :  *
    5798             :  * This is the same as the C function GDALMDArrayGetView()
    5799             :  *
    5800             :  * @param viewExpr Expression expressing basic slicing and indexing, or field
    5801             :  * access.
    5802             :  * @return a new array, that holds a reference to the original one, and thus is
    5803             :  * a view of it (not a copy), or nullptr in case of error.
    5804             :  */
    5805             : // clang-format on
    5806             : 
    5807             : std::shared_ptr<GDALMDArray>
    5808         654 : GDALMDArray::GetView(const std::string &viewExpr) const
    5809             : {
    5810        1308 :     std::vector<ViewSpec> viewSpecs;
    5811        1308 :     return GetView(viewExpr, true, viewSpecs);
    5812             : }
    5813             : 
    5814             : //! @cond Doxygen_Suppress
    5815             : std::shared_ptr<GDALMDArray>
    5816         726 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
    5817             :                      std::vector<ViewSpec> &viewSpecs) const
    5818             : {
    5819        1452 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    5820         726 :     if (!self)
    5821             :     {
    5822           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    5823             :                  "Driver implementation issue: m_pSelf not set !");
    5824           1 :         return nullptr;
    5825             :     }
    5826         725 :     std::string curExpr(viewExpr);
    5827             :     while (true)
    5828             :     {
    5829         728 :         if (curExpr.empty() || curExpr[0] != '[')
    5830             :         {
    5831           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    5832             :                      "Slice string should start with ['");
    5833         725 :             return nullptr;
    5834             :         }
    5835             : 
    5836         726 :         std::string fieldName;
    5837             :         size_t endExpr;
    5838         726 :         if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
    5839             :         {
    5840         103 :             if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
    5841             :             {
    5842           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5843             :                          "Field access not allowed on non-compound data type");
    5844           2 :                 return nullptr;
    5845             :             }
    5846         101 :             size_t idx = 2;
    5847         985 :             for (; idx < curExpr.size(); idx++)
    5848             :             {
    5849         984 :                 const char ch = curExpr[idx];
    5850         984 :                 if (ch == curExpr[1])
    5851         100 :                     break;
    5852         884 :                 if (ch == '\\' && idx + 1 < curExpr.size())
    5853             :                 {
    5854           1 :                     fieldName += curExpr[idx + 1];
    5855           1 :                     idx++;
    5856             :                 }
    5857             :                 else
    5858             :                 {
    5859         883 :                     fieldName += ch;
    5860             :                 }
    5861             :             }
    5862         101 :             if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
    5863             :             {
    5864           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5865             :                          "Invalid field access specification");
    5866           2 :                 return nullptr;
    5867             :             }
    5868          99 :             endExpr = idx + 1;
    5869             :         }
    5870             :         else
    5871             :         {
    5872         623 :             endExpr = curExpr.find(']');
    5873             :         }
    5874         722 :         if (endExpr == std::string::npos)
    5875             :         {
    5876           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
    5877           1 :             return nullptr;
    5878             :         }
    5879         721 :         if (endExpr == 1)
    5880             :         {
    5881           1 :             CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
    5882           1 :             return nullptr;
    5883             :         }
    5884         720 :         std::string activeSlice(curExpr.substr(1, endExpr - 1));
    5885             : 
    5886         720 :         if (!fieldName.empty())
    5887             :         {
    5888         198 :             ViewSpec viewSpec;
    5889          99 :             viewSpec.m_osFieldName = fieldName;
    5890          99 :             viewSpecs.emplace_back(std::move(viewSpec));
    5891             :         }
    5892             : 
    5893         720 :         auto newArray = !fieldName.empty()
    5894             :                             ? CreateFieldNameExtractArray(self, fieldName)
    5895             :                             : CreateSlicedArray(self, viewExpr, activeSlice,
    5896         720 :                                                 bRenameDimensions, viewSpecs);
    5897             : 
    5898         720 :         if (endExpr == curExpr.size() - 1)
    5899             :         {
    5900         717 :             return newArray;
    5901             :         }
    5902           3 :         self = std::move(newArray);
    5903           3 :         curExpr = curExpr.substr(endExpr + 1);
    5904           3 :     }
    5905             : }
    5906             : 
    5907             : //! @endcond
    5908             : 
    5909             : std::shared_ptr<GDALMDArray>
    5910          19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
    5911             : {
    5912          19 :     std::string osExpr("[");
    5913          19 :     bool bFirst = true;
    5914          45 :     for (const auto &idx : indices)
    5915             :     {
    5916          26 :         if (!bFirst)
    5917           7 :             osExpr += ',';
    5918          26 :         bFirst = false;
    5919          26 :         osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
    5920             :     }
    5921          57 :     return GetView(osExpr + ']');
    5922             : }
    5923             : 
    5924             : /************************************************************************/
    5925             : /*                            operator[]                                */
    5926             : /************************************************************************/
    5927             : 
    5928             : /** Return a view of the array using field access
    5929             :  *
    5930             :  * Equivalent of GetView("['fieldName']")
    5931             :  *
    5932             :  * \note When operating on a shared_ptr, use (*array)["fieldName"] syntax.
    5933             :  */
    5934             : std::shared_ptr<GDALMDArray>
    5935           2 : GDALMDArray::operator[](const std::string &fieldName) const
    5936             : {
    5937           2 :     return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
    5938           4 :                                             .replaceAll('\\', "\\\\")
    5939           4 :                                             .replaceAll('\'', "\\\'")
    5940           6 :                                             .c_str()));
    5941             : }
    5942             : 
    5943             : /************************************************************************/
    5944             : /*                      GDALMDArrayTransposed                           */
    5945             : /************************************************************************/
    5946             : 
    5947             : class GDALMDArrayTransposed final : public GDALPamMDArray
    5948             : {
    5949             :   private:
    5950             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5951             :     std::vector<int> m_anMapNewAxisToOldAxis{};
    5952             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    5953             : 
    5954             :     mutable std::vector<GUInt64> m_parentStart;
    5955             :     mutable std::vector<size_t> m_parentCount;
    5956             :     mutable std::vector<GInt64> m_parentStep;
    5957             :     mutable std::vector<GPtrDiff_t> m_parentStride;
    5958             : 
    5959             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
    5960             :                              const GInt64 *arrayStep,
    5961             :                              const GPtrDiff_t *bufferStride) const;
    5962             : 
    5963             :     static std::string
    5964          84 :     MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
    5965             :     {
    5966          84 :         std::string ret;
    5967          84 :         ret += '[';
    5968         312 :         for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
    5969             :         {
    5970         228 :             if (i > 0)
    5971         144 :                 ret += ',';
    5972         228 :             ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
    5973             :         }
    5974          84 :         ret += ']';
    5975          84 :         return ret;
    5976             :     }
    5977             : 
    5978             :   protected:
    5979          42 :     GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
    5980             :                           const std::vector<int> &anMapNewAxisToOldAxis,
    5981             :                           std::vector<std::shared_ptr<GDALDimension>> &&dims)
    5982          84 :         : GDALAbstractMDArray(std::string(),
    5983          84 :                               "Transposed view of " + poParent->GetFullName() +
    5984          84 :                                   " along " +
    5985          42 :                                   MappingToStr(anMapNewAxisToOldAxis)),
    5986          84 :           GDALPamMDArray(std::string(),
    5987          84 :                          "Transposed view of " + poParent->GetFullName() +
    5988         168 :                              " along " + MappingToStr(anMapNewAxisToOldAxis),
    5989          84 :                          GDALPamMultiDim::GetPAM(poParent),
    5990             :                          poParent->GetContext()),
    5991          42 :           m_poParent(std::move(poParent)),
    5992             :           m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
    5993          42 :           m_dims(std::move(dims)),
    5994          42 :           m_parentStart(m_poParent->GetDimensionCount()),
    5995          42 :           m_parentCount(m_poParent->GetDimensionCount()),
    5996          42 :           m_parentStep(m_poParent->GetDimensionCount()),
    5997         336 :           m_parentStride(m_poParent->GetDimensionCount())
    5998             :     {
    5999          42 :     }
    6000             : 
    6001             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6002             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    6003             :                const GDALExtendedDataType &bufferDataType,
    6004             :                void *pDstBuffer) const override;
    6005             : 
    6006             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    6007             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    6008             :                 const GDALExtendedDataType &bufferDataType,
    6009             :                 const void *pSrcBuffer) override;
    6010             : 
    6011             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6012             :                      CSLConstList papszOptions) const override;
    6013             : 
    6014             :   public:
    6015             :     static std::shared_ptr<GDALMDArrayTransposed>
    6016          42 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    6017             :            const std::vector<int> &anMapNewAxisToOldAxis)
    6018             :     {
    6019          42 :         const auto &parentDims(poParent->GetDimensions());
    6020          84 :         std::vector<std::shared_ptr<GDALDimension>> dims;
    6021         156 :         for (const auto iOldAxis : anMapNewAxisToOldAxis)
    6022             :         {
    6023         114 :             if (iOldAxis < 0)
    6024             :             {
    6025           1 :                 dims.push_back(std::make_shared<GDALDimension>(
    6026           2 :                     std::string(), "newaxis", std::string(), std::string(), 1));
    6027             :             }
    6028             :             else
    6029             :             {
    6030         113 :                 dims.emplace_back(parentDims[iOldAxis]);
    6031             :             }
    6032             :         }
    6033             : 
    6034             :         auto newAr(
    6035             :             std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
    6036          42 :                 poParent, anMapNewAxisToOldAxis, std::move(dims))));
    6037          42 :         newAr->SetSelf(newAr);
    6038          84 :         return newAr;
    6039             :     }
    6040             : 
    6041           1 :     bool IsWritable() const override
    6042             :     {
    6043           1 :         return m_poParent->IsWritable();
    6044             :     }
    6045             : 
    6046          84 :     const std::string &GetFilename() const override
    6047             :     {
    6048          84 :         return m_poParent->GetFilename();
    6049             :     }
    6050             : 
    6051             :     const std::vector<std::shared_ptr<GDALDimension>> &
    6052         358 :     GetDimensions() const override
    6053             :     {
    6054         358 :         return m_dims;
    6055             :     }
    6056             : 
    6057         141 :     const GDALExtendedDataType &GetDataType() const override
    6058             :     {
    6059         141 :         return m_poParent->GetDataType();
    6060             :     }
    6061             : 
    6062           4 :     const std::string &GetUnit() const override
    6063             :     {
    6064           4 :         return m_poParent->GetUnit();
    6065             :     }
    6066             : 
    6067           5 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    6068             :     {
    6069          10 :         auto poSrcSRS = m_poParent->GetSpatialRef();
    6070           5 :         if (!poSrcSRS)
    6071           2 :             return nullptr;
    6072           6 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
    6073           6 :         std::vector<int> dstMapping;
    6074           9 :         for (int srcAxis : srcMapping)
    6075             :         {
    6076           6 :             bool bFound = false;
    6077          14 :             for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
    6078             :             {
    6079          14 :                 if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
    6080             :                 {
    6081           6 :                     dstMapping.push_back(static_cast<int>(i) + 1);
    6082           6 :                     bFound = true;
    6083           6 :                     break;
    6084             :                 }
    6085             :             }
    6086           6 :             if (!bFound)
    6087             :             {
    6088           0 :                 dstMapping.push_back(0);
    6089             :             }
    6090             :         }
    6091           6 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
    6092           3 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
    6093           3 :         return poClone;
    6094             :     }
    6095             : 
    6096           4 :     const void *GetRawNoDataValue() const override
    6097             :     {
    6098           4 :         return m_poParent->GetRawNoDataValue();
    6099             :     }
    6100             : 
    6101             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
    6102             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
    6103             : 
    6104           4 :     double GetOffset(bool *pbHasOffset,
    6105             :                      GDALDataType *peStorageType) const override
    6106             :     {
    6107           4 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    6108             :     }
    6109             : 
    6110           4 :     double GetScale(bool *pbHasScale,
    6111             :                     GDALDataType *peStorageType) const override
    6112             :     {
    6113           4 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    6114             :     }
    6115             : 
    6116             :     // bool SetOffset(double dfOffset) override { return
    6117             :     // m_poParent->SetOffset(dfOffset); }
    6118             : 
    6119             :     // bool SetScale(double dfScale) override { return
    6120             :     // m_poParent->SetScale(dfScale); }
    6121             : 
    6122           3 :     std::vector<GUInt64> GetBlockSize() const override
    6123             :     {
    6124           3 :         std::vector<GUInt64> ret(GetDimensionCount());
    6125           6 :         const auto parentBlockSize(m_poParent->GetBlockSize());
    6126          11 :         for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
    6127             :         {
    6128           8 :             const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
    6129           8 :             if (iOldAxis >= 0)
    6130             :             {
    6131           7 :                 ret[i] = parentBlockSize[iOldAxis];
    6132             :             }
    6133             :         }
    6134           6 :         return ret;
    6135             :     }
    6136             : 
    6137             :     std::shared_ptr<GDALAttribute>
    6138           1 :     GetAttribute(const std::string &osName) const override
    6139             :     {
    6140           1 :         return m_poParent->GetAttribute(osName);
    6141             :     }
    6142             : 
    6143             :     std::vector<std::shared_ptr<GDALAttribute>>
    6144           6 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    6145             :     {
    6146           6 :         return m_poParent->GetAttributes(papszOptions);
    6147             :     }
    6148             : };
    6149             : 
    6150             : /************************************************************************/
    6151             : /*                         PrepareParentArrays()                        */
    6152             : /************************************************************************/
    6153             : 
    6154          47 : void GDALMDArrayTransposed::PrepareParentArrays(
    6155             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    6156             :     const GPtrDiff_t *bufferStride) const
    6157             : {
    6158         176 :     for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
    6159             :     {
    6160         129 :         const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
    6161         129 :         if (iOldAxis >= 0)
    6162             :         {
    6163         128 :             m_parentStart[iOldAxis] = arrayStartIdx[i];
    6164         128 :             m_parentCount[iOldAxis] = count[i];
    6165         128 :             if (arrayStep)  // only null when called from IAdviseRead()
    6166             :             {
    6167         126 :                 m_parentStep[iOldAxis] = arrayStep[i];
    6168             :             }
    6169         128 :             if (bufferStride)  // only null when called from IAdviseRead()
    6170             :             {
    6171         126 :                 m_parentStride[iOldAxis] = bufferStride[i];
    6172             :             }
    6173             :         }
    6174             :     }
    6175          47 : }
    6176             : 
    6177             : /************************************************************************/
    6178             : /*                             IRead()                                  */
    6179             : /************************************************************************/
    6180             : 
    6181          44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
    6182             :                                   const size_t *count, const GInt64 *arrayStep,
    6183             :                                   const GPtrDiff_t *bufferStride,
    6184             :                                   const GDALExtendedDataType &bufferDataType,
    6185             :                                   void *pDstBuffer) const
    6186             : {
    6187          44 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    6188          88 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
    6189          44 :                             m_parentStep.data(), m_parentStride.data(),
    6190          44 :                             bufferDataType, pDstBuffer);
    6191             : }
    6192             : 
    6193             : /************************************************************************/
    6194             : /*                            IWrite()                                  */
    6195             : /************************************************************************/
    6196             : 
    6197           2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
    6198             :                                    const size_t *count, const GInt64 *arrayStep,
    6199             :                                    const GPtrDiff_t *bufferStride,
    6200             :                                    const GDALExtendedDataType &bufferDataType,
    6201             :                                    const void *pSrcBuffer)
    6202             : {
    6203           2 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    6204           4 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
    6205           2 :                              m_parentStep.data(), m_parentStride.data(),
    6206           2 :                              bufferDataType, pSrcBuffer);
    6207             : }
    6208             : 
    6209             : /************************************************************************/
    6210             : /*                             IAdviseRead()                            */
    6211             : /************************************************************************/
    6212             : 
    6213           1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
    6214             :                                         const size_t *count,
    6215             :                                         CSLConstList papszOptions) const
    6216             : {
    6217           1 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
    6218           1 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
    6219           1 :                                   papszOptions);
    6220             : }
    6221             : 
    6222             : /************************************************************************/
    6223             : /*                           Transpose()                                */
    6224             : /************************************************************************/
    6225             : 
    6226             : /** Return a view of the array whose axis have been reordered.
    6227             :  *
    6228             :  * The anMapNewAxisToOldAxis parameter should contain all the values between 0
    6229             :  * and GetDimensionCount() - 1, and each only once.
    6230             :  * -1 can be used as a special index value to ask for the insertion of a new
    6231             :  * axis of size 1.
    6232             :  * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
    6233             :  * index of one of its dimension, it corresponds to the axis of index
    6234             :  * anMapNewAxisToOldAxis[i] from the current array.
    6235             :  *
    6236             :  * This is similar to the numpy.transpose() method
    6237             :  *
    6238             :  * The returned array holds a reference to the original one, and thus is
    6239             :  * a view of it (not a copy). If the content of the original array changes,
    6240             :  * the content of the view array too. The view can be written if the underlying
    6241             :  * array is writable.
    6242             :  *
    6243             :  * Note that I/O performance in such a transposed view might be poor.
    6244             :  *
    6245             :  * This is the same as the C function GDALMDArrayTranspose().
    6246             :  *
    6247             :  * @return a new array, that holds a reference to the original one, and thus is
    6248             :  * a view of it (not a copy), or nullptr in case of error.
    6249             :  */
    6250             : std::shared_ptr<GDALMDArray>
    6251          50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
    6252             : {
    6253         100 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    6254          50 :     if (!self)
    6255             :     {
    6256           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6257             :                  "Driver implementation issue: m_pSelf not set !");
    6258           0 :         return nullptr;
    6259             :     }
    6260          50 :     const int nDims = static_cast<int>(GetDimensionCount());
    6261         100 :     std::vector<bool> alreadyUsedOldAxis(nDims, false);
    6262          50 :     int nCountOldAxis = 0;
    6263         179 :     for (const auto iOldAxis : anMapNewAxisToOldAxis)
    6264             :     {
    6265         133 :         if (iOldAxis < -1 || iOldAxis >= nDims)
    6266             :         {
    6267           3 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
    6268           4 :             return nullptr;
    6269             :         }
    6270         130 :         if (iOldAxis >= 0)
    6271             :         {
    6272         128 :             if (alreadyUsedOldAxis[iOldAxis])
    6273             :             {
    6274           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
    6275             :                          iOldAxis);
    6276           1 :                 return nullptr;
    6277             :             }
    6278         127 :             alreadyUsedOldAxis[iOldAxis] = true;
    6279         127 :             nCountOldAxis++;
    6280             :         }
    6281             :     }
    6282          46 :     if (nCountOldAxis != nDims)
    6283             :     {
    6284           4 :         CPLError(CE_Failure, CPLE_AppDefined,
    6285             :                  "One or several original axis missing");
    6286           4 :         return nullptr;
    6287             :     }
    6288          42 :     return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
    6289             : }
    6290             : 
    6291             : /************************************************************************/
    6292             : /*                             IRead()                                  */
    6293             : /************************************************************************/
    6294             : 
    6295          16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
    6296             :                                 const size_t *count, const GInt64 *arrayStep,
    6297             :                                 const GPtrDiff_t *bufferStride,
    6298             :                                 const GDALExtendedDataType &bufferDataType,
    6299             :                                 void *pDstBuffer) const
    6300             : {
    6301          16 :     const double dfScale = m_dfScale;
    6302          16 :     const double dfOffset = m_dfOffset;
    6303          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
    6304             :     const auto dtDouble =
    6305          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
    6306          16 :     const size_t nDTSize = dtDouble.GetSize();
    6307          16 :     const bool bTempBufferNeeded = (dtDouble != bufferDataType);
    6308             : 
    6309          16 :     double adfSrcNoData[2] = {0, 0};
    6310          16 :     if (m_bHasNoData)
    6311             :     {
    6312           9 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
    6313           9 :                                         m_poParent->GetDataType(),
    6314             :                                         &adfSrcNoData[0], dtDouble);
    6315             :     }
    6316             : 
    6317          16 :     const auto nDims = GetDimensions().size();
    6318          16 :     if (nDims == 0)
    6319             :     {
    6320             :         double adfVal[2];
    6321           9 :         if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
    6322             :                               dtDouble, &adfVal[0]))
    6323             :         {
    6324           0 :             return false;
    6325             :         }
    6326           9 :         if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
    6327             :         {
    6328           6 :             adfVal[0] = adfVal[0] * dfScale + dfOffset;
    6329           6 :             if (bDTIsComplex)
    6330             :             {
    6331           2 :                 adfVal[1] = adfVal[1] * dfScale + dfOffset;
    6332             :             }
    6333           6 :             GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
    6334             :                                             bufferDataType);
    6335             :         }
    6336             :         else
    6337             :         {
    6338           3 :             GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
    6339             :                                             pDstBuffer, bufferDataType);
    6340             :         }
    6341           9 :         return true;
    6342             :     }
    6343             : 
    6344          14 :     std::vector<GPtrDiff_t> actualBufferStrideVector;
    6345           7 :     const GPtrDiff_t *actualBufferStridePtr = bufferStride;
    6346           7 :     void *pTempBuffer = pDstBuffer;
    6347           7 :     if (bTempBufferNeeded)
    6348             :     {
    6349           2 :         size_t nElts = 1;
    6350           2 :         actualBufferStrideVector.resize(nDims);
    6351           7 :         for (size_t i = 0; i < nDims; i++)
    6352           5 :             nElts *= count[i];
    6353           2 :         actualBufferStrideVector.back() = 1;
    6354           5 :         for (size_t i = nDims - 1; i > 0;)
    6355             :         {
    6356           3 :             --i;
    6357           3 :             actualBufferStrideVector[i] =
    6358           3 :                 actualBufferStrideVector[i + 1] * count[i + 1];
    6359             :         }
    6360           2 :         actualBufferStridePtr = actualBufferStrideVector.data();
    6361           2 :         pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
    6362           2 :         if (!pTempBuffer)
    6363           0 :             return false;
    6364             :     }
    6365           7 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
    6366             :                           actualBufferStridePtr, dtDouble, pTempBuffer))
    6367             :     {
    6368           0 :         if (bTempBufferNeeded)
    6369           0 :             VSIFree(pTempBuffer);
    6370           0 :         return false;
    6371             :     }
    6372             : 
    6373             :     struct Stack
    6374             :     {
    6375             :         size_t nIters = 0;
    6376             :         double *src_ptr = nullptr;
    6377             :         GByte *dst_ptr = nullptr;
    6378             :         GPtrDiff_t src_inc_offset = 0;
    6379             :         GPtrDiff_t dst_inc_offset = 0;
    6380             :     };
    6381             : 
    6382           7 :     std::vector<Stack> stack(nDims);
    6383           7 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    6384          23 :     for (size_t i = 0; i < nDims; i++)
    6385             :     {
    6386          32 :         stack[i].src_inc_offset =
    6387          16 :             actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
    6388          16 :         stack[i].dst_inc_offset =
    6389          16 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6390             :     }
    6391           7 :     stack[0].src_ptr = static_cast<double *>(pTempBuffer);
    6392           7 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    6393             : 
    6394           7 :     size_t dimIdx = 0;
    6395           7 :     const size_t nDimsMinus1 = nDims - 1;
    6396             :     GByte abyDstNoData[16];
    6397           7 :     CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
    6398           7 :     GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
    6399             :                                     bufferDataType);
    6400             : 
    6401          37 : lbl_next_depth:
    6402          37 :     if (dimIdx == nDimsMinus1)
    6403             :     {
    6404          25 :         auto nIters = count[dimIdx];
    6405          25 :         double *padfVal = stack[dimIdx].src_ptr;
    6406          25 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
    6407             :         while (true)
    6408             :         {
    6409          92 :             if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
    6410             :             {
    6411          88 :                 padfVal[0] = padfVal[0] * dfScale + dfOffset;
    6412          88 :                 if (bDTIsComplex)
    6413             :                 {
    6414           1 :                     padfVal[1] = padfVal[1] * dfScale + dfOffset;
    6415             :                 }
    6416          88 :                 if (bTempBufferNeeded)
    6417             :                 {
    6418          29 :                     GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
    6419             :                                                     dst_ptr, bufferDataType);
    6420             :                 }
    6421             :             }
    6422             :             else
    6423             :             {
    6424           4 :                 memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
    6425             :             }
    6426             : 
    6427          92 :             if ((--nIters) == 0)
    6428          25 :                 break;
    6429          67 :             padfVal += stack[dimIdx].src_inc_offset;
    6430          67 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    6431             :         }
    6432             :     }
    6433             :     else
    6434             :     {
    6435          12 :         stack[dimIdx].nIters = count[dimIdx];
    6436             :         while (true)
    6437             :         {
    6438          30 :             dimIdx++;
    6439          30 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    6440          30 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6441          30 :             goto lbl_next_depth;
    6442          30 :         lbl_return_to_caller:
    6443          30 :             dimIdx--;
    6444          30 :             if ((--stack[dimIdx].nIters) == 0)
    6445          12 :                 break;
    6446          18 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    6447          18 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6448             :         }
    6449             :     }
    6450          37 :     if (dimIdx > 0)
    6451          30 :         goto lbl_return_to_caller;
    6452             : 
    6453           7 :     if (bTempBufferNeeded)
    6454           2 :         VSIFree(pTempBuffer);
    6455           7 :     return true;
    6456             : }
    6457             : 
    6458             : /************************************************************************/
    6459             : /*                             IWrite()                                 */
    6460             : /************************************************************************/
    6461             : 
    6462          16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
    6463             :                                  const size_t *count, const GInt64 *arrayStep,
    6464             :                                  const GPtrDiff_t *bufferStride,
    6465             :                                  const GDALExtendedDataType &bufferDataType,
    6466             :                                  const void *pSrcBuffer)
    6467             : {
    6468          16 :     const double dfScale = m_dfScale;
    6469          16 :     const double dfOffset = m_dfOffset;
    6470          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
    6471             :     const auto dtDouble =
    6472          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
    6473          16 :     const size_t nDTSize = dtDouble.GetSize();
    6474          16 :     const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
    6475             :     const bool bSelfAndParentHaveNoData =
    6476          16 :         m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
    6477          16 :     double dfNoData = 0;
    6478          16 :     if (m_bHasNoData)
    6479             :     {
    6480           7 :         GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
    6481             :                         &dfNoData, GDT_Float64, 0, 1);
    6482             :     }
    6483             : 
    6484          16 :     double adfSrcNoData[2] = {0, 0};
    6485          16 :     if (bSelfAndParentHaveNoData)
    6486             :     {
    6487           7 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
    6488           7 :                                         m_poParent->GetDataType(),
    6489             :                                         &adfSrcNoData[0], dtDouble);
    6490             :     }
    6491             : 
    6492          16 :     const auto nDims = GetDimensions().size();
    6493          16 :     if (nDims == 0)
    6494             :     {
    6495             :         double adfVal[2];
    6496          10 :         GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
    6497             :                                         dtDouble);
    6498          16 :         if (bSelfAndParentHaveNoData &&
    6499           6 :             (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
    6500             :         {
    6501           4 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
    6502           2 :                                      bufferStride, m_poParent->GetDataType(),
    6503           4 :                                      m_poParent->GetRawNoDataValue());
    6504             :         }
    6505             :         else
    6506             :         {
    6507           8 :             adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
    6508           8 :             if (bDTIsComplex)
    6509             :             {
    6510           2 :                 adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
    6511             :             }
    6512           8 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
    6513           8 :                                      bufferStride, dtDouble, &adfVal[0]);
    6514             :         }
    6515             :     }
    6516             : 
    6517          12 :     std::vector<GPtrDiff_t> tmpBufferStrideVector;
    6518           6 :     size_t nElts = 1;
    6519           6 :     tmpBufferStrideVector.resize(nDims);
    6520          20 :     for (size_t i = 0; i < nDims; i++)
    6521          14 :         nElts *= count[i];
    6522           6 :     tmpBufferStrideVector.back() = 1;
    6523          14 :     for (size_t i = nDims - 1; i > 0;)
    6524             :     {
    6525           8 :         --i;
    6526           8 :         tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
    6527             :     }
    6528           6 :     const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
    6529           6 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
    6530           6 :     if (!pTempBuffer)
    6531           0 :         return false;
    6532             : 
    6533             :     struct Stack
    6534             :     {
    6535             :         size_t nIters = 0;
    6536             :         double *dst_ptr = nullptr;
    6537             :         const GByte *src_ptr = nullptr;
    6538             :         GPtrDiff_t src_inc_offset = 0;
    6539             :         GPtrDiff_t dst_inc_offset = 0;
    6540             :     };
    6541             : 
    6542           6 :     std::vector<Stack> stack(nDims);
    6543           6 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    6544          20 :     for (size_t i = 0; i < nDims; i++)
    6545             :     {
    6546          28 :         stack[i].dst_inc_offset =
    6547          14 :             tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
    6548          14 :         stack[i].src_inc_offset =
    6549          14 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6550             :     }
    6551           6 :     stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
    6552           6 :     stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
    6553             : 
    6554           6 :     size_t dimIdx = 0;
    6555           6 :     const size_t nDimsMinus1 = nDims - 1;
    6556             : 
    6557          34 : lbl_next_depth:
    6558          34 :     if (dimIdx == nDimsMinus1)
    6559             :     {
    6560          23 :         auto nIters = count[dimIdx];
    6561          23 :         double *dst_ptr = stack[dimIdx].dst_ptr;
    6562          23 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
    6563             :         while (true)
    6564             :         {
    6565             :             double adfVal[2];
    6566             :             const double *padfSrcVal;
    6567          86 :             if (bIsBufferDataTypeNativeDataType)
    6568             :             {
    6569          50 :                 padfSrcVal = reinterpret_cast<const double *>(src_ptr);
    6570             :             }
    6571             :             else
    6572             :             {
    6573          36 :                 GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
    6574             :                                                 &adfVal[0], dtDouble);
    6575          36 :                 padfSrcVal = adfVal;
    6576             :             }
    6577             : 
    6578         148 :             if (bSelfAndParentHaveNoData &&
    6579          62 :                 (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
    6580             :             {
    6581           3 :                 dst_ptr[0] = adfSrcNoData[0];
    6582           3 :                 if (bDTIsComplex)
    6583             :                 {
    6584           1 :                     dst_ptr[1] = adfSrcNoData[1];
    6585             :                 }
    6586             :             }
    6587             :             else
    6588             :             {
    6589          83 :                 dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
    6590          83 :                 if (bDTIsComplex)
    6591             :                 {
    6592           1 :                     dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
    6593             :                 }
    6594             :             }
    6595             : 
    6596          86 :             if ((--nIters) == 0)
    6597          23 :                 break;
    6598          63 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    6599          63 :             src_ptr += stack[dimIdx].src_inc_offset;
    6600          63 :         }
    6601             :     }
    6602             :     else
    6603             :     {
    6604          11 :         stack[dimIdx].nIters = count[dimIdx];
    6605             :         while (true)
    6606             :         {
    6607          28 :             dimIdx++;
    6608          28 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    6609          28 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6610          28 :             goto lbl_next_depth;
    6611          28 :         lbl_return_to_caller:
    6612          28 :             dimIdx--;
    6613          28 :             if ((--stack[dimIdx].nIters) == 0)
    6614          11 :                 break;
    6615          17 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    6616          17 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6617             :         }
    6618             :     }
    6619          34 :     if (dimIdx > 0)
    6620          28 :         goto lbl_return_to_caller;
    6621             : 
    6622             :     // If the parent array is not double/complex-double, then convert the
    6623             :     // values to it, before calling Write(), as some implementations can be
    6624             :     // very slow when doing the type conversion.
    6625           6 :     const auto &eParentDT = m_poParent->GetDataType();
    6626           6 :     const size_t nParentDTSize = eParentDT.GetSize();
    6627           6 :     if (nParentDTSize <= nDTSize / 2)
    6628             :     {
    6629             :         // Copy in-place by making sure that source and target do not overlap
    6630           6 :         const auto eNumericDT = dtDouble.GetNumericDataType();
    6631           6 :         const auto eParentNumericDT = eParentDT.GetNumericDataType();
    6632             : 
    6633             :         // Copy first element
    6634             :         {
    6635           6 :             std::vector<GByte> abyTemp(nParentDTSize);
    6636           6 :             GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
    6637           6 :                             static_cast<int>(nDTSize), &abyTemp[0],
    6638             :                             eParentNumericDT, static_cast<int>(nParentDTSize),
    6639             :                             1);
    6640           6 :             memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
    6641             :         }
    6642             :         // Remaining elements
    6643          86 :         for (size_t i = 1; i < nElts; ++i)
    6644             :         {
    6645          80 :             GDALCopyWords64(
    6646          80 :                 static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
    6647          80 :                 static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
    6648             :                 eParentNumericDT, 0, 1);
    6649             :         }
    6650             :     }
    6651             : 
    6652             :     const bool ret =
    6653           6 :         m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
    6654             :                           eParentDT, pTempBuffer);
    6655             : 
    6656           6 :     VSIFree(pTempBuffer);
    6657           6 :     return ret;
    6658             : }
    6659             : 
    6660             : /************************************************************************/
    6661             : /*                           GetUnscaled()                              */
    6662             : /************************************************************************/
    6663             : 
    6664             : /** Return an array that is the unscaled version of the current one.
    6665             :  *
    6666             :  * That is each value of the unscaled array will be
    6667             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    6668             :  *
    6669             :  * Starting with GDAL 3.3, the Write() method is implemented and will convert
    6670             :  * from unscaled values to raw values.
    6671             :  *
    6672             :  * This is the same as the C function GDALMDArrayGetUnscaled().
    6673             :  *
    6674             :  * @param dfOverriddenScale Custom scale value instead of GetScale()
    6675             :  * @param dfOverriddenOffset Custom offset value instead of GetOffset()
    6676             :  * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
    6677             :  * @return a new array, that holds a reference to the original one, and thus is
    6678             :  * a view of it (not a copy), or nullptr in case of error.
    6679             :  */
    6680             : std::shared_ptr<GDALMDArray>
    6681          17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
    6682             :                          double dfOverriddenDstNodata) const
    6683             : {
    6684          34 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    6685          17 :     if (!self)
    6686             :     {
    6687           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6688             :                  "Driver implementation issue: m_pSelf not set !");
    6689           0 :         return nullptr;
    6690             :     }
    6691          17 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    6692             :     {
    6693           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6694             :                  "GetUnscaled() only supports numeric data type");
    6695           0 :         return nullptr;
    6696             :     }
    6697             :     const double dfScale =
    6698          17 :         std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
    6699             :     const double dfOffset =
    6700          17 :         std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
    6701          17 :     if (dfScale == 1.0 && dfOffset == 0.0)
    6702           4 :         return self;
    6703             : 
    6704          13 :     GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
    6705          13 :                            ? GDT_CFloat64
    6706          13 :                            : GDT_Float64;
    6707          13 :     if (dfOverriddenScale == -1 && dfOverriddenOffset == 0)
    6708             :     {
    6709           1 :         if (GetDataType().GetNumericDataType() == GDT_Float16)
    6710           0 :             eDT = GDT_Float16;
    6711           1 :         if (GetDataType().GetNumericDataType() == GDT_Float32)
    6712           1 :             eDT = GDT_Float32;
    6713             :     }
    6714             : 
    6715          26 :     return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
    6716          13 :                                        dfOverriddenDstNodata, eDT);
    6717             : }
    6718             : 
    6719             : /************************************************************************/
    6720             : /*                         GDALMDArrayMask                              */
    6721             : /************************************************************************/
    6722             : 
    6723             : class GDALMDArrayMask final : public GDALPamMDArray
    6724             : {
    6725             :   private:
    6726             :     std::shared_ptr<GDALMDArray> m_poParent{};
    6727             :     GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_Byte)};
    6728             :     double m_dfMissingValue = 0.0;
    6729             :     bool m_bHasMissingValue = false;
    6730             :     double m_dfFillValue = 0.0;
    6731             :     bool m_bHasFillValue = false;
    6732             :     double m_dfValidMin = 0.0;
    6733             :     bool m_bHasValidMin = false;
    6734             :     double m_dfValidMax = 0.0;
    6735             :     bool m_bHasValidMax = false;
    6736             :     std::vector<uint32_t> m_anValidFlagMasks{};
    6737             :     std::vector<uint32_t> m_anValidFlagValues{};
    6738             : 
    6739             :     bool Init(CSLConstList papszOptions);
    6740             : 
    6741             :     template <typename Type>
    6742             :     void
    6743             :     ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
    6744             :                  const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    6745             :                  const void *pTempBuffer,
    6746             :                  const GDALExtendedDataType &oTmpBufferDT,
    6747             :                  const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
    6748             : 
    6749             :   protected:
    6750          48 :     explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
    6751          96 :         : GDALAbstractMDArray(std::string(),
    6752          96 :                               "Mask of " + poParent->GetFullName()),
    6753          96 :           GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
    6754          96 :                          GDALPamMultiDim::GetPAM(poParent),
    6755             :                          poParent->GetContext()),
    6756         240 :           m_poParent(std::move(poParent))
    6757             :     {
    6758          48 :     }
    6759             : 
    6760             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6761             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    6762             :                const GDALExtendedDataType &bufferDataType,
    6763             :                void *pDstBuffer) const override;
    6764             : 
    6765           0 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6766             :                      CSLConstList papszOptions) const override
    6767             :     {
    6768           0 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
    6769             :     }
    6770             : 
    6771             :   public:
    6772             :     static std::shared_ptr<GDALMDArrayMask>
    6773             :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    6774             :            CSLConstList papszOptions);
    6775             : 
    6776           1 :     bool IsWritable() const override
    6777             :     {
    6778           1 :         return false;
    6779             :     }
    6780             : 
    6781          54 :     const std::string &GetFilename() const override
    6782             :     {
    6783          54 :         return m_poParent->GetFilename();
    6784             :     }
    6785             : 
    6786             :     const std::vector<std::shared_ptr<GDALDimension>> &
    6787         382 :     GetDimensions() const override
    6788             :     {
    6789         382 :         return m_poParent->GetDimensions();
    6790             :     }
    6791             : 
    6792         138 :     const GDALExtendedDataType &GetDataType() const override
    6793             :     {
    6794         138 :         return m_dt;
    6795             :     }
    6796             : 
    6797           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    6798             :     {
    6799           1 :         return m_poParent->GetSpatialRef();
    6800             :     }
    6801             : 
    6802           2 :     std::vector<GUInt64> GetBlockSize() const override
    6803             :     {
    6804           2 :         return m_poParent->GetBlockSize();
    6805             :     }
    6806             : };
    6807             : 
    6808             : /************************************************************************/
    6809             : /*                    GDALMDArrayMask::Create()                         */
    6810             : /************************************************************************/
    6811             : 
    6812             : /* static */ std::shared_ptr<GDALMDArrayMask>
    6813          48 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
    6814             :                         CSLConstList papszOptions)
    6815             : {
    6816          96 :     auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
    6817          48 :     newAr->SetSelf(newAr);
    6818          48 :     if (!newAr->Init(papszOptions))
    6819           6 :         return nullptr;
    6820          42 :     return newAr;
    6821             : }
    6822             : 
    6823             : /************************************************************************/
    6824             : /*                    GDALMDArrayMask::Init()                           */
    6825             : /************************************************************************/
    6826             : 
    6827          48 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
    6828             : {
    6829             :     const auto GetSingleValNumericAttr =
    6830         192 :         [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
    6831             :     {
    6832         576 :         auto poAttr = m_poParent->GetAttribute(pszAttrName);
    6833         192 :         if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
    6834             :         {
    6835          22 :             const auto anDimSizes = poAttr->GetDimensionsSize();
    6836          21 :             if (anDimSizes.empty() ||
    6837          10 :                 (anDimSizes.size() == 1 && anDimSizes[0] == 1))
    6838             :             {
    6839          11 :                 bHasVal = true;
    6840          11 :                 dfVal = poAttr->ReadAsDouble();
    6841             :             }
    6842             :         }
    6843         192 :     };
    6844             : 
    6845          48 :     GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
    6846          48 :                             m_dfMissingValue);
    6847          48 :     GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
    6848          48 :     GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
    6849          48 :     GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
    6850             : 
    6851             :     {
    6852         144 :         auto poValidRange = m_poParent->GetAttribute("valid_range");
    6853          54 :         if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
    6854          60 :             poValidRange->GetDimensionsSize()[0] == 2 &&
    6855           6 :             poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
    6856             :         {
    6857           6 :             m_bHasValidMin = true;
    6858           6 :             m_bHasValidMax = true;
    6859           6 :             auto vals = poValidRange->ReadAsDoubleArray();
    6860           6 :             CPLAssert(vals.size() == 2);
    6861           6 :             m_dfValidMin = vals[0];
    6862           6 :             m_dfValidMax = vals[1];
    6863             :         }
    6864             :     }
    6865             : 
    6866             :     // Take into account
    6867             :     // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
    6868             :     // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
    6869             :     const char *pszUnmaskFlags =
    6870          48 :         CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
    6871          48 :     if (pszUnmaskFlags)
    6872             :     {
    6873             :         const auto IsScalarStringAttr =
    6874          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
    6875             :         {
    6876          26 :             return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
    6877          26 :                    (poAttr->GetDimensionsSize().empty() ||
    6878          13 :                     (poAttr->GetDimensionsSize().size() == 1 &&
    6879          26 :                      poAttr->GetDimensionsSize()[0] == 1));
    6880             :         };
    6881             : 
    6882          28 :         auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
    6883          14 :         if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
    6884             :         {
    6885           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6886             :                      "UNMASK_FLAGS option specified but array has no "
    6887             :                      "flag_meanings attribute");
    6888           1 :             return false;
    6889             :         }
    6890          13 :         const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
    6891          13 :         if (!pszFlagMeanings)
    6892             :         {
    6893           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6894             :                      "Cannot read flag_meanings attribute");
    6895           1 :             return false;
    6896             :         }
    6897             : 
    6898             :         const auto IsSingleDimNumericAttr =
    6899          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
    6900             :         {
    6901          26 :             return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
    6902          26 :                    poAttr->GetDimensionsSize().size() == 1;
    6903             :         };
    6904             : 
    6905          24 :         auto poFlagValues = m_poParent->GetAttribute("flag_values");
    6906             :         const bool bHasFlagValues =
    6907          12 :             poFlagValues && IsSingleDimNumericAttr(poFlagValues);
    6908             : 
    6909          24 :         auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
    6910             :         const bool bHasFlagMasks =
    6911          12 :             poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
    6912             : 
    6913          12 :         if (!bHasFlagValues && !bHasFlagMasks)
    6914             :         {
    6915           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6916             :                      "Cannot find flag_values and/or flag_masks attribute");
    6917           1 :             return false;
    6918             :         }
    6919             : 
    6920             :         const CPLStringList aosUnmaskFlags(
    6921          11 :             CSLTokenizeString2(pszUnmaskFlags, ",", 0));
    6922             :         const CPLStringList aosFlagMeanings(
    6923          11 :             CSLTokenizeString2(pszFlagMeanings, " ", 0));
    6924             : 
    6925          11 :         if (bHasFlagValues)
    6926             :         {
    6927           7 :             const auto eType = poFlagValues->GetDataType().GetNumericDataType();
    6928             :             // We could support Int64 or UInt64, but more work...
    6929           7 :             if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
    6930           0 :                 eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
    6931             :             {
    6932           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    6933             :                          "Unsupported data type for flag_values attribute: %s",
    6934             :                          GDALGetDataTypeName(eType));
    6935           0 :                 return false;
    6936             :             }
    6937             :         }
    6938             : 
    6939          11 :         if (bHasFlagMasks)
    6940             :         {
    6941           6 :             const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
    6942             :             // We could support Int64 or UInt64, but more work...
    6943           6 :             if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
    6944           0 :                 eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
    6945             :             {
    6946           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    6947             :                          "Unsupported data type for flag_masks attribute: %s",
    6948             :                          GDALGetDataTypeName(eType));
    6949           0 :                 return false;
    6950             :             }
    6951             :         }
    6952             : 
    6953             :         const std::vector<double> adfValues(
    6954             :             bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
    6955          11 :                            : std::vector<double>());
    6956             :         const std::vector<double> adfMasks(
    6957             :             bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
    6958          11 :                           : std::vector<double>());
    6959             : 
    6960          18 :         if (bHasFlagValues &&
    6961           7 :             adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
    6962             :         {
    6963           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6964             :                      "Number of values in flag_values attribute is different "
    6965             :                      "from the one in flag_meanings");
    6966           1 :             return false;
    6967             :         }
    6968             : 
    6969          16 :         if (bHasFlagMasks &&
    6970           6 :             adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
    6971             :         {
    6972           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6973             :                      "Number of values in flag_masks attribute is different "
    6974             :                      "from the one in flag_meanings");
    6975           1 :             return false;
    6976             :         }
    6977             : 
    6978          19 :         for (int i = 0; i < aosUnmaskFlags.size(); ++i)
    6979             :         {
    6980          11 :             const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
    6981          11 :             if (nIdxFlag < 0)
    6982             :             {
    6983           1 :                 CPLError(
    6984             :                     CE_Failure, CPLE_AppDefined,
    6985             :                     "Cannot fing flag %s in flag_meanings = '%s' attribute",
    6986             :                     aosUnmaskFlags[i], pszFlagMeanings);
    6987           1 :                 return false;
    6988             :             }
    6989             : 
    6990          10 :             if (bHasFlagValues && adfValues[nIdxFlag] < 0)
    6991             :             {
    6992           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    6993             :                          "Invalid value in flag_values[%d] = %f", nIdxFlag,
    6994           0 :                          adfValues[nIdxFlag]);
    6995           0 :                 return false;
    6996             :             }
    6997             : 
    6998          10 :             if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
    6999             :             {
    7000           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    7001             :                          "Invalid value in flag_masks[%d] = %f", nIdxFlag,
    7002           0 :                          adfMasks[nIdxFlag]);
    7003           0 :                 return false;
    7004             :             }
    7005             : 
    7006          10 :             if (bHasFlagValues)
    7007             :             {
    7008          12 :                 m_anValidFlagValues.push_back(
    7009           6 :                     static_cast<uint32_t>(adfValues[nIdxFlag]));
    7010             :             }
    7011             : 
    7012          10 :             if (bHasFlagMasks)
    7013             :             {
    7014          12 :                 m_anValidFlagMasks.push_back(
    7015           6 :                     static_cast<uint32_t>(adfMasks[nIdxFlag]));
    7016             :             }
    7017             :         }
    7018             :     }
    7019             : 
    7020          42 :     return true;
    7021             : }
    7022             : 
    7023             : /************************************************************************/
    7024             : /*                             IRead()                                  */
    7025             : /************************************************************************/
    7026             : 
    7027          51 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    7028             :                             const GInt64 *arrayStep,
    7029             :                             const GPtrDiff_t *bufferStride,
    7030             :                             const GDALExtendedDataType &bufferDataType,
    7031             :                             void *pDstBuffer) const
    7032             : {
    7033          51 :     if (bufferDataType.GetClass() != GEDTC_NUMERIC)
    7034             :     {
    7035           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    7036             :                  "%s: only reading to a numeric data type is supported",
    7037             :                  __func__);
    7038           0 :         return false;
    7039             :     }
    7040          51 :     size_t nElts = 1;
    7041          51 :     const size_t nDims = GetDimensionCount();
    7042         102 :     std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
    7043         139 :     for (size_t i = 0; i < nDims; i++)
    7044          88 :         nElts *= count[i];
    7045          51 :     if (nDims > 0)
    7046             :     {
    7047          46 :         tmpBufferStrideVector.back() = 1;
    7048          88 :         for (size_t i = nDims - 1; i > 0;)
    7049             :         {
    7050          42 :             --i;
    7051          42 :             tmpBufferStrideVector[i] =
    7052          42 :                 tmpBufferStrideVector[i + 1] * count[i + 1];
    7053             :         }
    7054             :     }
    7055             : 
    7056             :     /* Optimized case: if we are an integer data type and that there is no */
    7057             :     /* attribute that can be used to set mask = 0, then fill the mask buffer */
    7058             :     /* directly */
    7059          49 :     if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
    7060          74 :         !m_bHasValidMax && m_anValidFlagValues.empty() &&
    7061          34 :         m_anValidFlagMasks.empty() &&
    7062         111 :         m_poParent->GetRawNoDataValue() == nullptr &&
    7063          11 :         GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
    7064             :     {
    7065           7 :         const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
    7066           7 :         if (bBufferDataTypeIsByte)  // Byte case
    7067             :         {
    7068           4 :             bool bContiguous = true;
    7069          10 :             for (size_t i = 0; i < nDims; i++)
    7070             :             {
    7071           7 :                 if (bufferStride[i] != tmpBufferStrideVector[i])
    7072             :                 {
    7073           1 :                     bContiguous = false;
    7074           1 :                     break;
    7075             :                 }
    7076             :             }
    7077           4 :             if (bContiguous)
    7078             :             {
    7079             :                 // CPLDebug("GDAL", "GetMask(): contiguous case");
    7080           3 :                 memset(pDstBuffer, 1, nElts);
    7081           3 :                 return true;
    7082             :             }
    7083             :         }
    7084             : 
    7085             :         struct Stack
    7086             :         {
    7087             :             size_t nIters = 0;
    7088             :             GByte *dst_ptr = nullptr;
    7089             :             GPtrDiff_t dst_inc_offset = 0;
    7090             :         };
    7091             : 
    7092           4 :         std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
    7093           4 :         const size_t nBufferDTSize = bufferDataType.GetSize();
    7094          13 :         for (size_t i = 0; i < nDims; i++)
    7095             :         {
    7096           9 :             stack[i].dst_inc_offset =
    7097           9 :                 static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    7098             :         }
    7099           4 :         stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    7100             : 
    7101           4 :         size_t dimIdx = 0;
    7102           4 :         const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
    7103             :         GByte abyOne[16];  // 16 is sizeof GDT_CFloat64
    7104           4 :         CPLAssert(nBufferDTSize <= 16);
    7105           4 :         const GByte flag = 1;
    7106           4 :         GDALCopyWords64(&flag, GDT_Byte, 0, abyOne,
    7107             :                         bufferDataType.GetNumericDataType(), 0, 1);
    7108             : 
    7109          28 :     lbl_next_depth:
    7110          28 :         if (dimIdx == nDimsMinus1)
    7111             :         {
    7112          19 :             auto nIters = nDims > 0 ? count[dimIdx] : 1;
    7113          19 :             GByte *dst_ptr = stack[dimIdx].dst_ptr;
    7114             : 
    7115             :             while (true)
    7116             :             {
    7117             :                 // cppcheck-suppress knownConditionTrueFalse
    7118          73 :                 if (bBufferDataTypeIsByte)
    7119             :                 {
    7120          24 :                     *dst_ptr = flag;
    7121             :                 }
    7122             :                 else
    7123             :                 {
    7124          49 :                     memcpy(dst_ptr, abyOne, nBufferDTSize);
    7125             :                 }
    7126             : 
    7127          73 :                 if ((--nIters) == 0)
    7128          19 :                     break;
    7129          54 :                 dst_ptr += stack[dimIdx].dst_inc_offset;
    7130             :             }
    7131             :         }
    7132             :         else
    7133             :         {
    7134           9 :             stack[dimIdx].nIters = count[dimIdx];
    7135             :             while (true)
    7136             :             {
    7137          24 :                 dimIdx++;
    7138          24 :                 stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    7139          24 :                 goto lbl_next_depth;
    7140          24 :             lbl_return_to_caller:
    7141          24 :                 dimIdx--;
    7142          24 :                 if ((--stack[dimIdx].nIters) == 0)
    7143           9 :                     break;
    7144          15 :                 stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    7145             :             }
    7146             :         }
    7147          28 :         if (dimIdx > 0)
    7148          24 :             goto lbl_return_to_caller;
    7149             : 
    7150           4 :         return true;
    7151             :     }
    7152             : 
    7153             :     const auto oTmpBufferDT =
    7154          44 :         GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
    7155             :             ? GDALExtendedDataType::Create(GDT_Float64)
    7156          88 :             : m_poParent->GetDataType();
    7157          44 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
    7158          44 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
    7159          44 :     if (!pTempBuffer)
    7160           0 :         return false;
    7161          88 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
    7162          44 :                           tmpBufferStrideVector.data(), oTmpBufferDT,
    7163             :                           pTempBuffer))
    7164             :     {
    7165           0 :         VSIFree(pTempBuffer);
    7166           0 :         return false;
    7167             :     }
    7168             : 
    7169          44 :     switch (oTmpBufferDT.GetNumericDataType())
    7170             :     {
    7171           7 :         case GDT_Byte:
    7172           7 :             ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
    7173             :                                 pTempBuffer, oTmpBufferDT,
    7174             :                                 tmpBufferStrideVector);
    7175           7 :             break;
    7176             : 
    7177           0 :         case GDT_Int8:
    7178           0 :             ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
    7179             :                                 pTempBuffer, oTmpBufferDT,
    7180             :                                 tmpBufferStrideVector);
    7181           0 :             break;
    7182             : 
    7183           1 :         case GDT_UInt16:
    7184           1 :             ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
    7185             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
    7186             :                                   tmpBufferStrideVector);
    7187           1 :             break;
    7188             : 
    7189          14 :         case GDT_Int16:
    7190          14 :             ReadInternal<GInt16>(count, bufferStride, bufferDataType,
    7191             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7192             :                                  tmpBufferStrideVector);
    7193          14 :             break;
    7194             : 
    7195           1 :         case GDT_UInt32:
    7196           1 :             ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
    7197             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
    7198             :                                   tmpBufferStrideVector);
    7199           1 :             break;
    7200             : 
    7201           5 :         case GDT_Int32:
    7202           5 :             ReadInternal<GInt32>(count, bufferStride, bufferDataType,
    7203             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7204             :                                  tmpBufferStrideVector);
    7205           5 :             break;
    7206             : 
    7207           0 :         case GDT_UInt64:
    7208           0 :             ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
    7209             :                                         pDstBuffer, pTempBuffer, oTmpBufferDT,
    7210             :                                         tmpBufferStrideVector);
    7211           0 :             break;
    7212             : 
    7213           0 :         case GDT_Int64:
    7214           0 :             ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
    7215             :                                        pDstBuffer, pTempBuffer, oTmpBufferDT,
    7216             :                                        tmpBufferStrideVector);
    7217           0 :             break;
    7218             : 
    7219           0 :         case GDT_Float16:
    7220           0 :             ReadInternal<GFloat16>(count, bufferStride, bufferDataType,
    7221             :                                    pDstBuffer, pTempBuffer, oTmpBufferDT,
    7222             :                                    tmpBufferStrideVector);
    7223           0 :             break;
    7224             : 
    7225           7 :         case GDT_Float32:
    7226           7 :             ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
    7227             :                                 pTempBuffer, oTmpBufferDT,
    7228             :                                 tmpBufferStrideVector);
    7229           7 :             break;
    7230             : 
    7231           9 :         case GDT_Float64:
    7232           9 :             ReadInternal<double>(count, bufferStride, bufferDataType,
    7233             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7234             :                                  tmpBufferStrideVector);
    7235           9 :             break;
    7236           0 :         case GDT_Unknown:
    7237             :         case GDT_CInt16:
    7238             :         case GDT_CInt32:
    7239             :         case GDT_CFloat16:
    7240             :         case GDT_CFloat32:
    7241             :         case GDT_CFloat64:
    7242             :         case GDT_TypeCount:
    7243           0 :             CPLAssert(false);
    7244             :             break;
    7245             :     }
    7246             : 
    7247          44 :     VSIFree(pTempBuffer);
    7248             : 
    7249          44 :     return true;
    7250             : }
    7251             : 
    7252             : /************************************************************************/
    7253             : /*                          IsValidForDT()                              */
    7254             : /************************************************************************/
    7255             : 
    7256          40 : template <typename Type> static bool IsValidForDT(double dfVal)
    7257             : {
    7258          40 :     if (std::isnan(dfVal))
    7259           0 :         return false;
    7260          40 :     if (dfVal < static_cast<double>(cpl::NumericLimits<Type>::lowest()))
    7261           0 :         return false;
    7262          40 :     if (dfVal > static_cast<double>(cpl::NumericLimits<Type>::max()))
    7263           0 :         return false;
    7264          40 :     return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
    7265             : }
    7266             : 
    7267           9 : template <> bool IsValidForDT<double>(double)
    7268             : {
    7269           9 :     return true;
    7270             : }
    7271             : 
    7272             : /************************************************************************/
    7273             : /*                              IsNan()                                 */
    7274             : /************************************************************************/
    7275             : 
    7276        1438 : template <typename Type> inline bool IsNan(Type)
    7277             : {
    7278        1438 :     return false;
    7279             : }
    7280             : 
    7281          65 : template <> bool IsNan<double>(double val)
    7282             : {
    7283          65 :     return std::isnan(val);
    7284             : }
    7285             : 
    7286          26 : template <> bool IsNan<float>(float val)
    7287             : {
    7288          26 :     return std::isnan(val);
    7289             : }
    7290             : 
    7291             : /************************************************************************/
    7292             : /*                         ReadInternal()                               */
    7293             : /************************************************************************/
    7294             : 
    7295             : template <typename Type>
    7296          44 : void GDALMDArrayMask::ReadInternal(
    7297             :     const size_t *count, const GPtrDiff_t *bufferStride,
    7298             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    7299             :     const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
    7300             :     const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
    7301             : {
    7302          44 :     const size_t nDims = GetDimensionCount();
    7303             : 
    7304         220 :     const auto castValue = [](bool &bHasVal, double dfVal) -> Type
    7305             :     {
    7306         220 :         if (bHasVal)
    7307             :         {
    7308          49 :             if (IsValidForDT<Type>(dfVal))
    7309             :             {
    7310          49 :                 return static_cast<Type>(dfVal);
    7311             :             }
    7312             :             else
    7313             :             {
    7314           0 :                 bHasVal = false;
    7315             :             }
    7316             :         }
    7317         171 :         return 0;
    7318             :     };
    7319             : 
    7320          44 :     const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
    7321          44 :     bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
    7322             :     const Type nNoDataValue =
    7323          44 :         castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
    7324          44 :     bool bHasMissingValue = m_bHasMissingValue;
    7325          44 :     const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
    7326          44 :     bool bHasFillValue = m_bHasFillValue;
    7327          44 :     const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
    7328          44 :     bool bHasValidMin = m_bHasValidMin;
    7329          44 :     const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
    7330          44 :     bool bHasValidMax = m_bHasValidMax;
    7331          44 :     const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
    7332          44 :     const bool bHasValidFlags =
    7333          44 :         !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
    7334             : 
    7335         351 :     const auto IsValidFlag = [this](Type v)
    7336             :     {
    7337          54 :         if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
    7338             :         {
    7339          20 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
    7340             :             {
    7341          12 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
    7342             :                     m_anValidFlagValues[i])
    7343             :                 {
    7344           4 :                     return true;
    7345             :                 }
    7346             :             }
    7347             :         }
    7348          42 :         else if (!m_anValidFlagValues.empty())
    7349             :         {
    7350          49 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
    7351             :             {
    7352          29 :                 if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
    7353             :                 {
    7354           4 :                     return true;
    7355             :                 }
    7356             :             }
    7357             :         }
    7358             :         else /* if( !m_anValidFlagMasks.empty() ) */
    7359             :         {
    7360          31 :             for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
    7361             :             {
    7362          22 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
    7363             :                 {
    7364           9 :                     return true;
    7365             :                 }
    7366             :             }
    7367             :         }
    7368          37 :         return false;
    7369             :     };
    7370             : 
    7371             : #define GET_MASK_FOR_SAMPLE(v)                                                 \
    7372             :     static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
    7373             :                        !(bHasMissingValue && v == nMissingValue) &&            \
    7374             :                        !(bHasFillValue && v == nFillValue) &&                  \
    7375             :                        !(bHasValidMin && v < nValidMin) &&                     \
    7376             :                        !(bHasValidMax && v > nValidMax) &&                     \
    7377             :                        (!bHasValidFlags || IsValidFlag(v)));
    7378             : 
    7379          44 :     const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
    7380             :     /* Optimized case: Byte output and output buffer is contiguous */
    7381          44 :     if (bBufferDataTypeIsByte)
    7382             :     {
    7383          40 :         bool bContiguous = true;
    7384         103 :         for (size_t i = 0; i < nDims; i++)
    7385             :         {
    7386          64 :             if (bufferStride[i] != tmpBufferStrideVector[i])
    7387             :             {
    7388           1 :                 bContiguous = false;
    7389           1 :                 break;
    7390             :             }
    7391             :         }
    7392          40 :         if (bContiguous)
    7393             :         {
    7394          39 :             size_t nElts = 1;
    7395         102 :             for (size_t i = 0; i < nDims; i++)
    7396          63 :                 nElts *= count[i];
    7397             : 
    7398        1113 :             for (size_t i = 0; i < nElts; i++)
    7399             :             {
    7400        1074 :                 const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
    7401        1074 :                 static_cast<GByte *>(pDstBuffer)[i] =
    7402        1074 :                     GET_MASK_FOR_SAMPLE(*pSrc);
    7403             :             }
    7404          39 :             return;
    7405             :         }
    7406             :     }
    7407             : 
    7408           5 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
    7409             : 
    7410             :     struct Stack
    7411             :     {
    7412             :         size_t nIters = 0;
    7413             :         const GByte *src_ptr = nullptr;
    7414             :         GByte *dst_ptr = nullptr;
    7415             :         GPtrDiff_t src_inc_offset = 0;
    7416             :         GPtrDiff_t dst_inc_offset = 0;
    7417             :     };
    7418             : 
    7419          10 :     std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
    7420           5 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    7421          15 :     for (size_t i = 0; i < nDims; i++)
    7422             :     {
    7423          20 :         stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
    7424          10 :             tmpBufferStrideVector[i] * nTmpBufferDTSize);
    7425          10 :         stack[i].dst_inc_offset =
    7426          10 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    7427             :     }
    7428           5 :     stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
    7429           5 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    7430             : 
    7431           5 :     size_t dimIdx = 0;
    7432           5 :     const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
    7433             :     GByte abyZeroOrOne[2][16];  // 16 is sizeof GDT_CFloat64
    7434           5 :     CPLAssert(nBufferDTSize <= 16);
    7435          15 :     for (GByte flag = 0; flag <= 1; flag++)
    7436             :     {
    7437          10 :         GDALCopyWords64(&flag, m_dt.GetNumericDataType(), 0, abyZeroOrOne[flag],
    7438             :                         bufferDataType.GetNumericDataType(), 0, 1);
    7439             :     }
    7440             : 
    7441          43 : lbl_next_depth:
    7442          43 :     if (dimIdx == nDimsMinus1)
    7443             :     {
    7444          35 :         auto nIters = nDims > 0 ? count[dimIdx] : 1;
    7445          35 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
    7446          35 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
    7447             : 
    7448         420 :         while (true)
    7449             :         {
    7450         455 :             const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
    7451         455 :             const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
    7452             : 
    7453         455 :             if (bBufferDataTypeIsByte)
    7454             :             {
    7455          24 :                 *dst_ptr = flag;
    7456             :             }
    7457             :             else
    7458             :             {
    7459         431 :                 memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
    7460             :             }
    7461             : 
    7462         455 :             if ((--nIters) == 0)
    7463          35 :                 break;
    7464         420 :             src_ptr += stack[dimIdx].src_inc_offset;
    7465         420 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    7466             :         }
    7467             :     }
    7468             :     else
    7469             :     {
    7470           8 :         stack[dimIdx].nIters = count[dimIdx];
    7471             :         while (true)
    7472             :         {
    7473          38 :             dimIdx++;
    7474          38 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    7475          38 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    7476          38 :             goto lbl_next_depth;
    7477          38 :         lbl_return_to_caller:
    7478          38 :             dimIdx--;
    7479          38 :             if ((--stack[dimIdx].nIters) == 0)
    7480           8 :                 break;
    7481          30 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    7482          30 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    7483             :         }
    7484             :     }
    7485          43 :     if (dimIdx > 0)
    7486          38 :         goto lbl_return_to_caller;
    7487             : }
    7488             : 
    7489             : /************************************************************************/
    7490             : /*                            GetMask()                                 */
    7491             : /************************************************************************/
    7492             : 
    7493             : /** Return an array that is a mask for the current array
    7494             : 
    7495             :  This array will be of type Byte, with values set to 0 to indicate invalid
    7496             :  pixels of the current array, and values set to 1 to indicate valid pixels.
    7497             : 
    7498             :  The generic implementation honours the NoDataValue, as well as various
    7499             :  netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
    7500             :  and valid_range.
    7501             : 
    7502             :  Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
    7503             :  can be used to specify strings of the "flag_meanings" attribute
    7504             :  (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
    7505             :  for which pixels matching any of those flags will be set at 1 in the mask array,
    7506             :  and pixels matching none of those flags will be set at 0.
    7507             :  For example, let's consider the following netCDF variable defined with:
    7508             :  \verbatim
    7509             :  l2p_flags:valid_min = 0s ;
    7510             :  l2p_flags:valid_max = 256s ;
    7511             :  l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
    7512             :  l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
    7513             :  \endverbatim
    7514             : 
    7515             :  GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
    7516             :  - for pixel values *outside* valid_range [0,256], the mask value will be 0.
    7517             :  - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
    7518             :    will be 1.
    7519             :  - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
    7520             :    will be 0.
    7521             : 
    7522             :  This is the same as the C function GDALMDArrayGetMask().
    7523             : 
    7524             :  @param papszOptions NULL-terminated list of options, or NULL.
    7525             : 
    7526             :  @return a new array, that holds a reference to the original one, and thus is
    7527             :  a view of it (not a copy), or nullptr in case of error.
    7528             : */
    7529             : std::shared_ptr<GDALMDArray>
    7530          49 : GDALMDArray::GetMask(CSLConstList papszOptions) const
    7531             : {
    7532          98 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    7533          49 :     if (!self)
    7534             :     {
    7535           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    7536             :                  "Driver implementation issue: m_pSelf not set !");
    7537           0 :         return nullptr;
    7538             :     }
    7539          49 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    7540             :     {
    7541           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    7542             :                  "GetMask() only supports numeric data type");
    7543           1 :         return nullptr;
    7544             :     }
    7545          48 :     return GDALMDArrayMask::Create(self, papszOptions);
    7546             : }
    7547             : 
    7548             : /************************************************************************/
    7549             : /*                         IsRegularlySpaced()                          */
    7550             : /************************************************************************/
    7551             : 
    7552             : /** Returns whether an array is a 1D regularly spaced array.
    7553             :  *
    7554             :  * @param[out] dfStart     First value in the array
    7555             :  * @param[out] dfIncrement Increment/spacing between consecutive values.
    7556             :  * @return true if the array is regularly spaced.
    7557             :  */
    7558         323 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
    7559             : {
    7560         323 :     dfStart = 0;
    7561         323 :     dfIncrement = 0;
    7562         323 :     if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
    7563           0 :         return false;
    7564         323 :     const auto nSize = GetDimensions()[0]->GetSize();
    7565         323 :     if (nSize <= 1 || nSize > 10 * 1000 * 1000)
    7566           2 :         return false;
    7567             : 
    7568         321 :     size_t nCount = static_cast<size_t>(nSize);
    7569         642 :     std::vector<double> adfTmp;
    7570             :     try
    7571             :     {
    7572         321 :         adfTmp.resize(nCount);
    7573             :     }
    7574           0 :     catch (const std::exception &)
    7575             :     {
    7576           0 :         return false;
    7577             :     }
    7578             : 
    7579         321 :     GUInt64 anStart[1] = {0};
    7580         321 :     size_t anCount[1] = {nCount};
    7581             : 
    7582             :     const auto IsRegularlySpacedInternal =
    7583       89644 :         [&dfStart, &dfIncrement, &anCount, &adfTmp]()
    7584             :     {
    7585         419 :         dfStart = adfTmp[0];
    7586         419 :         dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
    7587         419 :         if (dfIncrement == 0)
    7588             :         {
    7589           3 :             return false;
    7590             :         }
    7591       22295 :         for (size_t i = 1; i < anCount[0]; i++)
    7592             :         {
    7593       21891 :             if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
    7594       21891 :                 1e-3 * fabs(dfIncrement))
    7595             :             {
    7596          12 :                 return false;
    7597             :             }
    7598             :         }
    7599         404 :         return true;
    7600         321 :     };
    7601             : 
    7602             :     // First try with the first block(s). This can avoid excessive processing
    7603             :     // time, for example with Zarr datasets.
    7604             :     // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
    7605             :     // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
    7606         321 :     const auto nBlockSize = GetBlockSize()[0];
    7607         321 :     if (nCount >= 5 && nBlockSize <= nCount / 2)
    7608             :     {
    7609             :         size_t nReducedCount =
    7610         101 :             std::max<size_t>(3, static_cast<size_t>(nBlockSize));
    7611         514 :         while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
    7612         413 :             nReducedCount *= 2;
    7613         101 :         anCount[0] = nReducedCount;
    7614         101 :         if (!Read(anStart, anCount, nullptr, nullptr,
    7615         202 :                   GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
    7616             :         {
    7617           0 :             return false;
    7618             :         }
    7619         101 :         if (!IsRegularlySpacedInternal())
    7620             :         {
    7621           3 :             return false;
    7622             :         }
    7623             : 
    7624             :         // Get next values
    7625          98 :         anStart[0] = nReducedCount;
    7626          98 :         anCount[0] = nCount - nReducedCount;
    7627             :     }
    7628             : 
    7629         318 :     if (!Read(anStart, anCount, nullptr, nullptr,
    7630         636 :               GDALExtendedDataType::Create(GDT_Float64),
    7631         318 :               &adfTmp[static_cast<size_t>(anStart[0])]))
    7632             :     {
    7633           0 :         return false;
    7634             :     }
    7635             : 
    7636         318 :     return IsRegularlySpacedInternal();
    7637             : }
    7638             : 
    7639             : /************************************************************************/
    7640             : /*                         GuessGeoTransform()                          */
    7641             : /************************************************************************/
    7642             : 
    7643             : /** Returns whether 2 specified dimensions form a geotransform
    7644             :  *
    7645             :  * @param nDimX                Index of the X axis.
    7646             :  * @param nDimY                Index of the Y axis.
    7647             :  * @param bPixelIsPoint        Whether the geotransform should be returned
    7648             :  *                             with the pixel-is-point (pixel-center) convention
    7649             :  *                             (bPixelIsPoint = true), or with the pixel-is-area
    7650             :  *                             (top left corner convention)
    7651             :  *                             (bPixelIsPoint = false)
    7652             :  * @param[out] gt              Computed geotransform
    7653             :  * @return true if a geotransform could be computed.
    7654             :  */
    7655         264 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
    7656             :                                     bool bPixelIsPoint,
    7657             :                                     GDALGeoTransform &gt) const
    7658             : {
    7659         264 :     const auto &dims(GetDimensions());
    7660         528 :     auto poVarX = dims[nDimX]->GetIndexingVariable();
    7661         528 :     auto poVarY = dims[nDimY]->GetIndexingVariable();
    7662         264 :     double dfXStart = 0.0;
    7663         264 :     double dfXSpacing = 0.0;
    7664         264 :     double dfYStart = 0.0;
    7665         264 :     double dfYSpacing = 0.0;
    7666         584 :     if (poVarX && poVarX->GetDimensionCount() == 1 &&
    7667         320 :         poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
    7668         458 :         poVarY && poVarY->GetDimensionCount() == 1 &&
    7669         149 :         poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
    7670         568 :         poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
    7671         144 :         poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
    7672             :     {
    7673         144 :         gt[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
    7674         144 :         gt[1] = dfXSpacing;
    7675         144 :         gt[2] = 0;
    7676         144 :         gt[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
    7677         144 :         gt[4] = 0;
    7678         144 :         gt[5] = dfYSpacing;
    7679         144 :         return true;
    7680             :     }
    7681         120 :     return false;
    7682             : }
    7683             : 
    7684             : /** Returns whether 2 specified dimensions form a geotransform
    7685             :  *
    7686             :  * @param nDimX                Index of the X axis.
    7687             :  * @param nDimY                Index of the Y axis.
    7688             :  * @param bPixelIsPoint        Whether the geotransform should be returned
    7689             :  *                             with the pixel-is-point (pixel-center) convention
    7690             :  *                             (bPixelIsPoint = true), or with the pixel-is-area
    7691             :  *                             (top left corner convention)
    7692             :  *                             (bPixelIsPoint = false)
    7693             :  * @param[out] adfGeoTransform Computed geotransform
    7694             :  * @return true if a geotransform could be computed.
    7695             :  */
    7696           0 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
    7697             :                                     bool bPixelIsPoint,
    7698             :                                     double adfGeoTransform[6]) const
    7699             : {
    7700           0 :     GDALGeoTransform *gt =
    7701             :         reinterpret_cast<GDALGeoTransform *>(adfGeoTransform);
    7702           0 :     return GuessGeoTransform(nDimX, nDimY, bPixelIsPoint, *gt);
    7703             : }
    7704             : 
    7705             : /************************************************************************/
    7706             : /*                       GDALMDArrayResampled                           */
    7707             : /************************************************************************/
    7708             : 
    7709             : class GDALMDArrayResampledDataset;
    7710             : 
    7711             : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
    7712             : {
    7713             :   protected:
    7714             :     CPLErr IReadBlock(int, int, void *) override;
    7715             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
    7716             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
    7717             :                      GDALDataType eBufType, GSpacing nPixelSpaceBuf,
    7718             :                      GSpacing nLineSpaceBuf,
    7719             :                      GDALRasterIOExtraArg *psExtraArg) override;
    7720             : 
    7721             :   public:
    7722             :     explicit GDALMDArrayResampledDatasetRasterBand(
    7723             :         GDALMDArrayResampledDataset *poDSIn);
    7724             : 
    7725             :     double GetNoDataValue(int *pbHasNoData) override;
    7726             : };
    7727             : 
    7728             : class GDALMDArrayResampledDataset final : public GDALPamDataset
    7729             : {
    7730             :     friend class GDALMDArrayResampled;
    7731             :     friend class GDALMDArrayResampledDatasetRasterBand;
    7732             : 
    7733             :     std::shared_ptr<GDALMDArray> m_poArray;
    7734             :     const size_t m_iXDim;
    7735             :     const size_t m_iYDim;
    7736             :     GDALGeoTransform m_gt{};
    7737             :     bool m_bHasGT = false;
    7738             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
    7739             : 
    7740             :     std::vector<GUInt64> m_anOffset{};
    7741             :     std::vector<size_t> m_anCount{};
    7742             :     std::vector<GPtrDiff_t> m_anStride{};
    7743             : 
    7744             :     std::string m_osFilenameLong{};
    7745             :     std::string m_osFilenameLat{};
    7746             : 
    7747             :   public:
    7748          24 :     GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
    7749             :                                 size_t iXDim, size_t iYDim)
    7750          24 :         : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
    7751          24 :           m_anOffset(m_poArray->GetDimensionCount(), 0),
    7752          24 :           m_anCount(m_poArray->GetDimensionCount(), 1),
    7753          72 :           m_anStride(m_poArray->GetDimensionCount(), 0)
    7754             :     {
    7755          24 :         const auto &dims(m_poArray->GetDimensions());
    7756             : 
    7757          24 :         nRasterYSize = static_cast<int>(
    7758          24 :             std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
    7759          24 :         nRasterXSize = static_cast<int>(
    7760          24 :             std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
    7761             : 
    7762          24 :         m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false, m_gt);
    7763             : 
    7764          24 :         SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
    7765          24 :     }
    7766             : 
    7767             :     ~GDALMDArrayResampledDataset() override;
    7768             : 
    7769          43 :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override
    7770             :     {
    7771          43 :         gt = m_gt;
    7772          43 :         return m_bHasGT ? CE_None : CE_Failure;
    7773             :     }
    7774             : 
    7775         105 :     const OGRSpatialReference *GetSpatialRef() const override
    7776             :     {
    7777         105 :         m_poSRS = m_poArray->GetSpatialRef();
    7778         105 :         if (m_poSRS)
    7779             :         {
    7780          79 :             m_poSRS.reset(m_poSRS->Clone());
    7781         158 :             auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
    7782         237 :             for (auto &m : axisMapping)
    7783             :             {
    7784         158 :                 if (m == static_cast<int>(m_iXDim) + 1)
    7785          79 :                     m = 1;
    7786          79 :                 else if (m == static_cast<int>(m_iYDim) + 1)
    7787          79 :                     m = 2;
    7788             :             }
    7789          79 :             m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
    7790             :         }
    7791         105 :         return m_poSRS.get();
    7792             :     }
    7793             : 
    7794           5 :     void SetGeolocationArray(const std::string &osFilenameLong,
    7795             :                              const std::string &osFilenameLat)
    7796             :     {
    7797           5 :         m_osFilenameLong = osFilenameLong;
    7798           5 :         m_osFilenameLat = osFilenameLat;
    7799          10 :         CPLStringList aosGeoLoc;
    7800           5 :         aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
    7801           5 :         aosGeoLoc.SetNameValue("LINE_STEP", "1");
    7802           5 :         aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
    7803           5 :         aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
    7804           5 :         aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG);  // FIXME?
    7805           5 :         aosGeoLoc.SetNameValue("X_BAND", "1");
    7806           5 :         aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
    7807           5 :         aosGeoLoc.SetNameValue("Y_BAND", "1");
    7808           5 :         aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
    7809           5 :         aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
    7810           5 :         SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
    7811           5 :     }
    7812             : };
    7813             : 
    7814          48 : GDALMDArrayResampledDataset::~GDALMDArrayResampledDataset()
    7815             : {
    7816          24 :     if (!m_osFilenameLong.empty())
    7817           5 :         VSIUnlink(m_osFilenameLong.c_str());
    7818          24 :     if (!m_osFilenameLat.empty())
    7819           5 :         VSIUnlink(m_osFilenameLat.c_str());
    7820          48 : }
    7821             : 
    7822             : /************************************************************************/
    7823             : /*                   GDALMDArrayResampledDatasetRasterBand()            */
    7824             : /************************************************************************/
    7825             : 
    7826          24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
    7827          24 :     GDALMDArrayResampledDataset *poDSIn)
    7828             : {
    7829          24 :     const auto &poArray(poDSIn->m_poArray);
    7830          24 :     const auto blockSize(poArray->GetBlockSize());
    7831          24 :     nBlockYSize = (blockSize[poDSIn->m_iYDim])
    7832          24 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    7833          13 :                                                   blockSize[poDSIn->m_iYDim]))
    7834          24 :                       : 1;
    7835          24 :     nBlockXSize = blockSize[poDSIn->m_iXDim]
    7836          13 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    7837          13 :                                                   blockSize[poDSIn->m_iXDim]))
    7838          24 :                       : poDSIn->GetRasterXSize();
    7839          24 :     eDataType = poArray->GetDataType().GetNumericDataType();
    7840          24 :     eAccess = poDSIn->eAccess;
    7841          24 : }
    7842             : 
    7843             : /************************************************************************/
    7844             : /*                           GetNoDataValue()                           */
    7845             : /************************************************************************/
    7846             : 
    7847          54 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
    7848             : {
    7849          54 :     auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
    7850          54 :     const auto &poArray(l_poDS->m_poArray);
    7851          54 :     bool bHasNodata = false;
    7852          54 :     double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
    7853          54 :     if (pbHasNoData)
    7854          48 :         *pbHasNoData = bHasNodata;
    7855          54 :     return dfRes;
    7856             : }
    7857             : 
    7858             : /************************************************************************/
    7859             : /*                            IReadBlock()                              */
    7860             : /************************************************************************/
    7861             : 
    7862           0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
    7863             :                                                          int nBlockYOff,
    7864             :                                                          void *pImage)
    7865             : {
    7866           0 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    7867           0 :     const int nXOff = nBlockXOff * nBlockXSize;
    7868           0 :     const int nYOff = nBlockYOff * nBlockYSize;
    7869           0 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    7870           0 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    7871             :     GDALRasterIOExtraArg sExtraArg;
    7872           0 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    7873           0 :     return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    7874             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    7875           0 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    7876             : }
    7877             : 
    7878             : /************************************************************************/
    7879             : /*                            IRasterIO()                               */
    7880             : /************************************************************************/
    7881             : 
    7882          32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
    7883             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
    7884             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
    7885             :     GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
    7886             :     GDALRasterIOExtraArg *psExtraArg)
    7887             : {
    7888          32 :     auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
    7889          32 :     const auto &poArray(l_poDS->m_poArray);
    7890          32 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
    7891          32 :     if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
    7892          32 :         nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
    7893          32 :         (nLineSpaceBuf % nBufferDTSize) == 0)
    7894             :     {
    7895          32 :         l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
    7896          32 :         l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
    7897          64 :         l_poDS->m_anStride[l_poDS->m_iXDim] =
    7898          32 :             static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
    7899             : 
    7900          32 :         l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
    7901          32 :         l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
    7902          64 :         l_poDS->m_anStride[l_poDS->m_iYDim] =
    7903          32 :             static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
    7904             : 
    7905          64 :         return poArray->Read(l_poDS->m_anOffset.data(),
    7906          32 :                              l_poDS->m_anCount.data(), nullptr,
    7907          32 :                              l_poDS->m_anStride.data(),
    7908          64 :                              GDALExtendedDataType::Create(eBufType), pData)
    7909          32 :                    ? CE_None
    7910          32 :                    : CE_Failure;
    7911             :     }
    7912           0 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    7913             :                                      pData, nBufXSize, nBufYSize, eBufType,
    7914           0 :                                      nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
    7915             : }
    7916             : 
    7917             : class GDALMDArrayResampled final : public GDALPamMDArray
    7918             : {
    7919             :   private:
    7920             :     std::shared_ptr<GDALMDArray> m_poParent{};
    7921             :     std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
    7922             :     std::vector<GUInt64> m_anBlockSize;
    7923             :     GDALExtendedDataType m_dt;
    7924             :     std::shared_ptr<OGRSpatialReference> m_poSRS{};
    7925             :     std::shared_ptr<GDALMDArray> m_poVarX{};
    7926             :     std::shared_ptr<GDALMDArray> m_poVarY{};
    7927             :     std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
    7928             :     std::unique_ptr<GDALDataset> m_poReprojectedDS{};
    7929             : 
    7930             :   protected:
    7931          21 :     GDALMDArrayResampled(
    7932             :         const std::shared_ptr<GDALMDArray> &poParent,
    7933             :         const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
    7934             :         const std::vector<GUInt64> &anBlockSize)
    7935          42 :         : GDALAbstractMDArray(std::string(),
    7936          42 :                               "Resampled view of " + poParent->GetFullName()),
    7937             :           GDALPamMDArray(
    7938          42 :               std::string(), "Resampled view of " + poParent->GetFullName(),
    7939          42 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
    7940          21 :           m_poParent(std::move(poParent)), m_apoDims(apoDims),
    7941         105 :           m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
    7942             :     {
    7943          21 :         CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
    7944          21 :         CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
    7945          21 :     }
    7946             : 
    7947             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    7948             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    7949             :                const GDALExtendedDataType &bufferDataType,
    7950             :                void *pDstBuffer) const override;
    7951             : 
    7952             :   public:
    7953             :     static std::shared_ptr<GDALMDArray>
    7954             :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    7955             :            const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
    7956             :            GDALRIOResampleAlg resampleAlg,
    7957             :            const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
    7958             : 
    7959          42 :     ~GDALMDArrayResampled() override
    7960          21 :     {
    7961             :         // First close the warped VRT
    7962          21 :         m_poReprojectedDS.reset();
    7963          21 :         m_poParentDS.reset();
    7964          42 :     }
    7965             : 
    7966          11 :     bool IsWritable() const override
    7967             :     {
    7968          11 :         return false;
    7969             :     }
    7970             : 
    7971          74 :     const std::string &GetFilename() const override
    7972             :     {
    7973          74 :         return m_poParent->GetFilename();
    7974             :     }
    7975             : 
    7976             :     const std::vector<std::shared_ptr<GDALDimension>> &
    7977         257 :     GetDimensions() const override
    7978             :     {
    7979         257 :         return m_apoDims;
    7980             :     }
    7981             : 
    7982         109 :     const GDALExtendedDataType &GetDataType() const override
    7983             :     {
    7984         109 :         return m_dt;
    7985             :     }
    7986             : 
    7987          21 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    7988             :     {
    7989          21 :         return m_poSRS;
    7990             :     }
    7991             : 
    7992          12 :     std::vector<GUInt64> GetBlockSize() const override
    7993             :     {
    7994          12 :         return m_anBlockSize;
    7995             :     }
    7996             : 
    7997             :     std::shared_ptr<GDALAttribute>
    7998           1 :     GetAttribute(const std::string &osName) const override
    7999             :     {
    8000           1 :         return m_poParent->GetAttribute(osName);
    8001             :     }
    8002             : 
    8003             :     std::vector<std::shared_ptr<GDALAttribute>>
    8004          12 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    8005             :     {
    8006          12 :         return m_poParent->GetAttributes(papszOptions);
    8007             :     }
    8008             : 
    8009           1 :     const std::string &GetUnit() const override
    8010             :     {
    8011           1 :         return m_poParent->GetUnit();
    8012             :     }
    8013             : 
    8014           1 :     const void *GetRawNoDataValue() const override
    8015             :     {
    8016           1 :         return m_poParent->GetRawNoDataValue();
    8017             :     }
    8018             : 
    8019           1 :     double GetOffset(bool *pbHasOffset,
    8020             :                      GDALDataType *peStorageType) const override
    8021             :     {
    8022           1 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    8023             :     }
    8024             : 
    8025           1 :     double GetScale(bool *pbHasScale,
    8026             :                     GDALDataType *peStorageType) const override
    8027             :     {
    8028           1 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    8029             :     }
    8030             : };
    8031             : 
    8032             : /************************************************************************/
    8033             : /*                   GDALMDArrayResampled::Create()                     */
    8034             : /************************************************************************/
    8035             : 
    8036          29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
    8037             :     const std::shared_ptr<GDALMDArray> &poParent,
    8038             :     const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
    8039             :     GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
    8040             :     CSLConstList /* papszOptions */)
    8041             : {
    8042          29 :     const char *pszResampleAlg = "nearest";
    8043          29 :     bool unsupported = false;
    8044          29 :     switch (resampleAlg)
    8045             :     {
    8046          16 :         case GRIORA_NearestNeighbour:
    8047          16 :             pszResampleAlg = "nearest";
    8048          16 :             break;
    8049           2 :         case GRIORA_Bilinear:
    8050           2 :             pszResampleAlg = "bilinear";
    8051           2 :             break;
    8052           5 :         case GRIORA_Cubic:
    8053           5 :             pszResampleAlg = "cubic";
    8054           5 :             break;
    8055           1 :         case GRIORA_CubicSpline:
    8056           1 :             pszResampleAlg = "cubicspline";
    8057           1 :             break;
    8058           1 :         case GRIORA_Lanczos:
    8059           1 :             pszResampleAlg = "lanczos";
    8060           1 :             break;
    8061           1 :         case GRIORA_Average:
    8062           1 :             pszResampleAlg = "average";
    8063           1 :             break;
    8064           1 :         case GRIORA_Mode:
    8065           1 :             pszResampleAlg = "mode";
    8066           1 :             break;
    8067           1 :         case GRIORA_Gauss:
    8068           1 :             unsupported = true;
    8069           1 :             break;
    8070           0 :         case GRIORA_RESERVED_START:
    8071           0 :             unsupported = true;
    8072           0 :             break;
    8073           0 :         case GRIORA_RESERVED_END:
    8074           0 :             unsupported = true;
    8075           0 :             break;
    8076           1 :         case GRIORA_RMS:
    8077           1 :             pszResampleAlg = "rms";
    8078           1 :             break;
    8079             :     }
    8080          29 :     if (unsupported)
    8081             :     {
    8082           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    8083             :                  "Unsupported resample method for GetResampled()");
    8084           1 :         return nullptr;
    8085             :     }
    8086             : 
    8087          28 :     if (poParent->GetDimensionCount() < 2)
    8088             :     {
    8089           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    8090             :                  "GetResampled() only supports 2 dimensions or more");
    8091           1 :         return nullptr;
    8092             :     }
    8093             : 
    8094          27 :     const auto &aoParentDims = poParent->GetDimensions();
    8095          27 :     if (apoNewDimsIn.size() != aoParentDims.size())
    8096             :     {
    8097           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8098             :                  "GetResampled(): apoNewDims size should be the same as "
    8099             :                  "GetDimensionCount()");
    8100           2 :         return nullptr;
    8101             :     }
    8102             : 
    8103          50 :     std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
    8104          25 :     apoNewDims.reserve(apoNewDimsIn.size());
    8105             : 
    8106          50 :     std::vector<GUInt64> anBlockSize;
    8107          25 :     anBlockSize.reserve(apoNewDimsIn.size());
    8108          50 :     const auto &anParentBlockSize = poParent->GetBlockSize();
    8109             : 
    8110          50 :     auto apoParentDims = poParent->GetDimensions();
    8111             :     // Special case for NASA EMIT datasets
    8112          30 :     const bool bYXBandOrder = (apoParentDims.size() == 3 &&
    8113           7 :                                apoParentDims[0]->GetName() == "downtrack" &&
    8114          32 :                                apoParentDims[1]->GetName() == "crosstrack" &&
    8115           2 :                                apoParentDims[2]->GetName() == "bands");
    8116             : 
    8117             :     const size_t iYDimParent =
    8118          25 :         bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
    8119             :     const size_t iXDimParent =
    8120          25 :         bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
    8121             : 
    8122          77 :     for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
    8123             :     {
    8124          53 :         if (i == iYDimParent || i == iXDimParent)
    8125          48 :             continue;
    8126           5 :         if (apoNewDimsIn[i] == nullptr)
    8127             :         {
    8128           3 :             apoNewDims.emplace_back(aoParentDims[i]);
    8129             :         }
    8130           3 :         else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
    8131           1 :                  apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
    8132             :         {
    8133           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    8134             :                      "GetResampled(): apoNewDims[%u] should be the same "
    8135             :                      "as its parent",
    8136             :                      i);
    8137           1 :             return nullptr;
    8138             :         }
    8139             :         else
    8140             :         {
    8141           1 :             apoNewDims.emplace_back(aoParentDims[i]);
    8142             :         }
    8143           4 :         anBlockSize.emplace_back(anParentBlockSize[i]);
    8144             :     }
    8145             : 
    8146             :     std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
    8147          48 :         new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
    8148             : 
    8149          24 :     double dfXStart = 0.0;
    8150          24 :     double dfXSpacing = 0.0;
    8151          24 :     bool gotXSpacing = false;
    8152          48 :     auto poNewDimX = apoNewDimsIn[iXDimParent];
    8153          24 :     if (poNewDimX)
    8154             :     {
    8155           4 :         if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
    8156             :         {
    8157           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    8158             :                      "Too big size for X dimension");
    8159           0 :             return nullptr;
    8160             :         }
    8161           4 :         auto var = poNewDimX->GetIndexingVariable();
    8162           4 :         if (var)
    8163             :         {
    8164           2 :             if (var->GetDimensionCount() != 1 ||
    8165           2 :                 var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
    8166           1 :                 !var->IsRegularlySpaced(dfXStart, dfXSpacing))
    8167             :             {
    8168           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    8169             :                          "New X dimension should be indexed by a regularly "
    8170             :                          "spaced variable");
    8171           0 :                 return nullptr;
    8172             :             }
    8173           1 :             gotXSpacing = true;
    8174             :         }
    8175             :     }
    8176             : 
    8177          24 :     double dfYStart = 0.0;
    8178          24 :     double dfYSpacing = 0.0;
    8179          48 :     auto poNewDimY = apoNewDimsIn[iYDimParent];
    8180          24 :     bool gotYSpacing = false;
    8181          24 :     if (poNewDimY)
    8182             :     {
    8183           4 :         if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
    8184             :         {
    8185           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    8186             :                      "Too big size for Y dimension");
    8187           0 :             return nullptr;
    8188             :         }
    8189           4 :         auto var = poNewDimY->GetIndexingVariable();
    8190           4 :         if (var)
    8191             :         {
    8192           2 :             if (var->GetDimensionCount() != 1 ||
    8193           2 :                 var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
    8194           1 :                 !var->IsRegularlySpaced(dfYStart, dfYSpacing))
    8195             :             {
    8196           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    8197             :                          "New Y dimension should be indexed by a regularly "
    8198             :                          "spaced variable");
    8199           0 :                 return nullptr;
    8200             :             }
    8201           1 :             gotYSpacing = true;
    8202             :         }
    8203             :     }
    8204             : 
    8205             :     // This limitation could probably be removed
    8206          24 :     if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
    8207             :     {
    8208           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    8209             :                  "Either none of new X or Y dimension should have an indexing "
    8210             :                  "variable, or both should both should have one.");
    8211           0 :         return nullptr;
    8212             :     }
    8213             : 
    8214          48 :     std::string osDstWKT;
    8215          24 :     if (poTargetSRS)
    8216             :     {
    8217           2 :         char *pszDstWKT = nullptr;
    8218           2 :         if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
    8219             :         {
    8220           0 :             CPLFree(pszDstWKT);
    8221           0 :             return nullptr;
    8222             :         }
    8223           2 :         osDstWKT = pszDstWKT;
    8224           2 :         CPLFree(pszDstWKT);
    8225             :     }
    8226             : 
    8227             :     // Use coordinate variables for geolocation array
    8228          48 :     const auto apoCoordinateVars = poParent->GetCoordinateVariables();
    8229          24 :     bool useGeolocationArray = false;
    8230          24 :     if (apoCoordinateVars.size() >= 2)
    8231             :     {
    8232           0 :         std::shared_ptr<GDALMDArray> poLongVar;
    8233           0 :         std::shared_ptr<GDALMDArray> poLatVar;
    8234          15 :         for (const auto &poCoordVar : apoCoordinateVars)
    8235             :         {
    8236          10 :             const auto &osName = poCoordVar->GetName();
    8237          30 :             const auto poAttr = poCoordVar->GetAttribute("standard_name");
    8238          20 :             std::string osStandardName;
    8239          12 :             if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
    8240           2 :                 poAttr->GetDimensionCount() == 0)
    8241             :             {
    8242           2 :                 const char *pszStandardName = poAttr->ReadAsString();
    8243           2 :                 if (pszStandardName)
    8244           2 :                     osStandardName = pszStandardName;
    8245             :             }
    8246          21 :             if (osName == "lon" || osName == "longitude" ||
    8247          21 :                 osName == "Longitude" || osStandardName == "longitude")
    8248             :             {
    8249           5 :                 poLongVar = poCoordVar;
    8250             :             }
    8251           6 :             else if (osName == "lat" || osName == "latitude" ||
    8252           6 :                      osName == "Latitude" || osStandardName == "latitude")
    8253             :             {
    8254           5 :                 poLatVar = poCoordVar;
    8255             :             }
    8256             :         }
    8257           5 :         if (poLatVar != nullptr && poLongVar != nullptr)
    8258             :         {
    8259           5 :             const auto longDimCount = poLongVar->GetDimensionCount();
    8260           5 :             const auto &longDims = poLongVar->GetDimensions();
    8261           5 :             const auto latDimCount = poLatVar->GetDimensionCount();
    8262           5 :             const auto &latDims = poLatVar->GetDimensions();
    8263           5 :             const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
    8264           5 :             const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
    8265           0 :             if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
    8266           5 :                 latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
    8267             :             {
    8268             :                 // Geolocation arrays are 1D, and of consistent size with
    8269             :                 // the variable
    8270           0 :                 useGeolocationArray = true;
    8271             :             }
    8272           1 :             else if ((longDimCount == 2 ||
    8273           6 :                       (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
    8274          10 :                      longDims[longDimCount - 2]->GetSize() == yDimSize &&
    8275          10 :                      longDims[longDimCount - 1]->GetSize() == xDimSize &&
    8276           1 :                      (latDimCount == 2 ||
    8277           6 :                       (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
    8278          15 :                      latDims[latDimCount - 2]->GetSize() == yDimSize &&
    8279           5 :                      latDims[latDimCount - 1]->GetSize() == xDimSize)
    8280             : 
    8281             :             {
    8282             :                 // Geolocation arrays are 2D (or 3D with first dimension of
    8283             :                 // size 1, as found in Sentinel 5P products), and of consistent
    8284             :                 // size with the variable
    8285           5 :                 useGeolocationArray = true;
    8286             :             }
    8287             :             else
    8288             :             {
    8289           0 :                 CPLDebug(
    8290             :                     "GDAL",
    8291             :                     "Longitude and latitude coordinate variables found, "
    8292             :                     "but their characteristics are not compatible of using "
    8293             :                     "them as geolocation arrays");
    8294             :             }
    8295           5 :             if (useGeolocationArray)
    8296             :             {
    8297          10 :                 CPLDebug("GDAL",
    8298             :                          "Setting geolocation array from variables %s and %s",
    8299           5 :                          poLongVar->GetName().c_str(),
    8300           5 :                          poLatVar->GetName().c_str());
    8301             :                 const std::string osFilenameLong =
    8302           5 :                     VSIMemGenerateHiddenFilename("longitude.tif");
    8303             :                 const std::string osFilenameLat =
    8304           5 :                     VSIMemGenerateHiddenFilename("latitude.tif");
    8305             :                 std::unique_ptr<GDALDataset> poTmpLongDS(
    8306             :                     longDimCount == 1
    8307           0 :                         ? poLongVar->AsClassicDataset(0, 0)
    8308          20 :                         : poLongVar->AsClassicDataset(longDimCount - 1,
    8309          15 :                                                       longDimCount - 2));
    8310           5 :                 auto hTIFFLongDS = GDALTranslate(
    8311             :                     osFilenameLong.c_str(),
    8312             :                     GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
    8313             :                 std::unique_ptr<GDALDataset> poTmpLatDS(
    8314           0 :                     latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
    8315          20 :                                      : poLatVar->AsClassicDataset(
    8316          15 :                                            latDimCount - 1, latDimCount - 2));
    8317           5 :                 auto hTIFFLatDS = GDALTranslate(
    8318             :                     osFilenameLat.c_str(),
    8319             :                     GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
    8320           5 :                 const bool bError =
    8321           5 :                     (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
    8322           5 :                 GDALClose(hTIFFLongDS);
    8323           5 :                 GDALClose(hTIFFLatDS);
    8324           5 :                 if (bError)
    8325             :                 {
    8326           0 :                     VSIUnlink(osFilenameLong.c_str());
    8327           0 :                     VSIUnlink(osFilenameLat.c_str());
    8328           0 :                     return nullptr;
    8329             :                 }
    8330             : 
    8331           5 :                 poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
    8332             :             }
    8333             :         }
    8334             :         else
    8335             :         {
    8336           0 :             CPLDebug("GDAL",
    8337             :                      "Coordinate variables available for %s, but "
    8338             :                      "longitude and/or latitude variables were not identified",
    8339           0 :                      poParent->GetName().c_str());
    8340             :         }
    8341             :     }
    8342             : 
    8343             :     // Build gdalwarp arguments
    8344          48 :     CPLStringList aosArgv;
    8345             : 
    8346          24 :     aosArgv.AddString("-of");
    8347          24 :     aosArgv.AddString("VRT");
    8348             : 
    8349          24 :     aosArgv.AddString("-r");
    8350          24 :     aosArgv.AddString(pszResampleAlg);
    8351             : 
    8352          24 :     if (!osDstWKT.empty())
    8353             :     {
    8354           2 :         aosArgv.AddString("-t_srs");
    8355           2 :         aosArgv.AddString(osDstWKT.c_str());
    8356             :     }
    8357             : 
    8358          24 :     if (useGeolocationArray)
    8359           5 :         aosArgv.AddString("-geoloc");
    8360             : 
    8361          24 :     if (gotXSpacing && gotYSpacing)
    8362             :     {
    8363           1 :         const double dfXMin = dfXStart - dfXSpacing / 2;
    8364             :         const double dfXMax =
    8365           1 :             dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
    8366           1 :         const double dfYMax = dfYStart - dfYSpacing / 2;
    8367             :         const double dfYMin =
    8368           1 :             dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
    8369           1 :         aosArgv.AddString("-te");
    8370           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
    8371           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
    8372           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
    8373           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
    8374             :     }
    8375             : 
    8376          24 :     if (poNewDimX && poNewDimY)
    8377             :     {
    8378           3 :         aosArgv.AddString("-ts");
    8379             :         aosArgv.AddString(
    8380           3 :             CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
    8381             :         aosArgv.AddString(
    8382           3 :             CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
    8383             :     }
    8384          21 :     else if (poNewDimX)
    8385             :     {
    8386           1 :         aosArgv.AddString("-ts");
    8387             :         aosArgv.AddString(
    8388           1 :             CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
    8389           1 :         aosArgv.AddString("0");
    8390             :     }
    8391          20 :     else if (poNewDimY)
    8392             :     {
    8393           1 :         aosArgv.AddString("-ts");
    8394           1 :         aosArgv.AddString("0");
    8395             :         aosArgv.AddString(
    8396           1 :             CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
    8397             :     }
    8398             : 
    8399             :     // Create a warped VRT dataset
    8400             :     GDALWarpAppOptions *psOptions =
    8401          24 :         GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
    8402          24 :     GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
    8403             :     std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
    8404          48 :         GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
    8405          24 :     GDALWarpAppOptionsFree(psOptions);
    8406          24 :     if (poReprojectedDS == nullptr)
    8407           3 :         return nullptr;
    8408             : 
    8409             :     int nBlockXSize;
    8410             :     int nBlockYSize;
    8411          21 :     poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    8412          21 :     anBlockSize.emplace_back(nBlockYSize);
    8413          21 :     anBlockSize.emplace_back(nBlockXSize);
    8414             : 
    8415          21 :     GDALGeoTransform gt;
    8416          21 :     CPLErr eErr = poReprojectedDS->GetGeoTransform(gt);
    8417          21 :     CPLAssert(eErr == CE_None);
    8418          21 :     CPL_IGNORE_RET_VAL(eErr);
    8419             : 
    8420             :     auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
    8421           0 :         std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
    8422          42 :         poReprojectedDS->GetRasterYSize());
    8423             :     auto varY = GDALMDArrayRegularlySpaced::Create(
    8424          63 :         std::string(), poDimY->GetName(), poDimY, gt[3] + gt[5] / 2, gt[5], 0);
    8425          21 :     poDimY->SetIndexingVariable(varY);
    8426             : 
    8427             :     auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
    8428           0 :         std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
    8429          42 :         poReprojectedDS->GetRasterXSize());
    8430             :     auto varX = GDALMDArrayRegularlySpaced::Create(
    8431          63 :         std::string(), poDimX->GetName(), poDimX, gt[0] + gt[1] / 2, gt[1], 0);
    8432          21 :     poDimX->SetIndexingVariable(varX);
    8433             : 
    8434          21 :     apoNewDims.emplace_back(poDimY);
    8435          21 :     apoNewDims.emplace_back(poDimX);
    8436             :     auto newAr(std::shared_ptr<GDALMDArrayResampled>(
    8437          42 :         new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
    8438          21 :     newAr->SetSelf(newAr);
    8439          21 :     if (poTargetSRS)
    8440             :     {
    8441           2 :         newAr->m_poSRS.reset(poTargetSRS->Clone());
    8442             :     }
    8443             :     else
    8444             :     {
    8445          19 :         newAr->m_poSRS = poParent->GetSpatialRef();
    8446             :     }
    8447          21 :     newAr->m_poVarX = varX;
    8448          21 :     newAr->m_poVarY = varY;
    8449          21 :     newAr->m_poReprojectedDS = std::move(poReprojectedDS);
    8450          21 :     newAr->m_poParentDS = std::move(poParentDS);
    8451             : 
    8452             :     // If the input array is y,x,band ordered, the above newAr is
    8453             :     // actually band,y,x ordered as it is more convenient for
    8454             :     // GDALMDArrayResampled::IRead() implementation. But transpose that
    8455             :     // array to the order of the input array
    8456          21 :     if (bYXBandOrder)
    8457           4 :         return newAr->Transpose(std::vector<int>{1, 2, 0});
    8458             : 
    8459          19 :     return newAr;
    8460             : }
    8461             : 
    8462             : /************************************************************************/
    8463             : /*                   GDALMDArrayResampled::IRead()                      */
    8464             : /************************************************************************/
    8465             : 
    8466          29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
    8467             :                                  const size_t *count, const GInt64 *arrayStep,
    8468             :                                  const GPtrDiff_t *bufferStride,
    8469             :                                  const GDALExtendedDataType &bufferDataType,
    8470             :                                  void *pDstBuffer) const
    8471             : {
    8472          29 :     if (bufferDataType.GetClass() != GEDTC_NUMERIC)
    8473           0 :         return false;
    8474             : 
    8475             :     struct Stack
    8476             :     {
    8477             :         size_t nIters = 0;
    8478             :         GByte *dst_ptr = nullptr;
    8479             :         GPtrDiff_t dst_inc_offset = 0;
    8480             :     };
    8481             : 
    8482          29 :     const auto nDims = GetDimensionCount();
    8483          58 :     std::vector<Stack> stack(nDims + 1);  // +1 to avoid -Wnull-dereference
    8484          29 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    8485          92 :     for (size_t i = 0; i < nDims; i++)
    8486             :     {
    8487          63 :         stack[i].dst_inc_offset =
    8488          63 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    8489             :     }
    8490          29 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    8491             : 
    8492          29 :     size_t dimIdx = 0;
    8493          29 :     const size_t iDimY = nDims - 2;
    8494          29 :     const size_t iDimX = nDims - 1;
    8495             :     // Use an array to avoid a false positive warning from CLang Static
    8496             :     // Analyzer about flushCaches being never read
    8497          29 :     bool flushCaches[] = {false};
    8498             :     const bool bYXBandOrder =
    8499          29 :         m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
    8500             : 
    8501          38 : lbl_next_depth:
    8502          38 :     if (dimIdx == iDimY)
    8503             :     {
    8504          33 :         if (flushCaches[0])
    8505             :         {
    8506           5 :             flushCaches[0] = false;
    8507             :             // When changing of 2D slice, flush GDAL 2D buffers
    8508           5 :             m_poParentDS->FlushCache(false);
    8509           5 :             m_poReprojectedDS->FlushCache(false);
    8510             :         }
    8511             : 
    8512          33 :         if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
    8513             :                                     GF_Read, iDimX, iDimY, arrayStartIdx, count,
    8514             :                                     arrayStep, bufferStride, bufferDataType,
    8515          33 :                                     stack[dimIdx].dst_ptr))
    8516             :         {
    8517           0 :             return false;
    8518             :         }
    8519             :     }
    8520             :     else
    8521             :     {
    8522           5 :         stack[dimIdx].nIters = count[dimIdx];
    8523           5 :         if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
    8524           5 :             arrayStartIdx[dimIdx])
    8525             :         {
    8526           1 :             flushCaches[0] = true;
    8527             :         }
    8528           5 :         m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
    8529           5 :             arrayStartIdx[dimIdx];
    8530             :         while (true)
    8531             :         {
    8532           9 :             dimIdx++;
    8533           9 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    8534           9 :             goto lbl_next_depth;
    8535           9 :         lbl_return_to_caller:
    8536           9 :             dimIdx--;
    8537           9 :             if ((--stack[dimIdx].nIters) == 0)
    8538           5 :                 break;
    8539           4 :             flushCaches[0] = true;
    8540           4 :             ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
    8541           4 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    8542             :         }
    8543             :     }
    8544          38 :     if (dimIdx > 0)
    8545           9 :         goto lbl_return_to_caller;
    8546             : 
    8547          29 :     return true;
    8548             : }
    8549             : 
    8550             : /************************************************************************/
    8551             : /*                           GetResampled()                             */
    8552             : /************************************************************************/
    8553             : 
    8554             : /** Return an array that is a resampled / reprojected view of the current array
    8555             :  *
    8556             :  * This is the same as the C function GDALMDArrayGetResampled().
    8557             :  *
    8558             :  * Currently this method can only resample along the last 2 dimensions, unless
    8559             :  * orthorectifying a NASA EMIT dataset.
    8560             :  *
    8561             :  * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
    8562             :  * geometry lookup table (GLT) is used by default for fast orthorectification.
    8563             :  *
    8564             :  * Options available are:
    8565             :  * <ul>
    8566             :  * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
    8567             :  * Can be set to NO to use generic reprojection method.
    8568             :  * </li>
    8569             :  * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
    8570             :  * orthorectification to take into account the value of the
    8571             :  * /sensor_band_parameters/good_wavelengths array to decide if slices of the
    8572             :  * current array along the band dimension are valid.</li>
    8573             :  * </ul>
    8574             :  *
    8575             :  * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
    8576             :  *                   apoNewDims[i] can be NULL to let the method automatically
    8577             :  *                   determine it.
    8578             :  * @param resampleAlg Resampling algorithm
    8579             :  * @param poTargetSRS Target SRS, or nullptr
    8580             :  * @param papszOptions NULL-terminated list of options, or NULL.
    8581             :  *
    8582             :  * @return a new array, that holds a reference to the original one, and thus is
    8583             :  * a view of it (not a copy), or nullptr in case of error.
    8584             :  *
    8585             :  * @since 3.4
    8586             :  */
    8587          38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
    8588             :     const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
    8589             :     GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
    8590             :     CSLConstList papszOptions) const
    8591             : {
    8592          76 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    8593          38 :     if (!self)
    8594             :     {
    8595           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8596             :                  "Driver implementation issue: m_pSelf not set !");
    8597           0 :         return nullptr;
    8598             :     }
    8599          38 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    8600             :     {
    8601           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8602             :                  "GetResampled() only supports numeric data type");
    8603           0 :         return nullptr;
    8604             :     }
    8605             : 
    8606             :     // Special case for NASA EMIT datasets
    8607          76 :     auto apoDims = GetDimensions();
    8608          36 :     if (poTargetSRS == nullptr &&
    8609          59 :         ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
    8610          20 :           apoDims[1]->GetName() == "crosstrack" &&
    8611          10 :           apoDims[2]->GetName() == "bands" &&
    8612          48 :           (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
    8613           1 :            apoNewDims ==
    8614          42 :                std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
    8615          30 :                                                            apoDims[2]})) ||
    8616          51 :          (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
    8617           3 :           apoDims[1]->GetName() == "crosstrack" &&
    8618          77 :           apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
    8619          13 :         CPLTestBool(CSLFetchNameValueDef(papszOptions,
    8620             :                                          "EMIT_ORTHORECTIFICATION", "YES")))
    8621             :     {
    8622           9 :         auto poRootGroup = GetRootGroup();
    8623           9 :         if (poRootGroup)
    8624             :         {
    8625          18 :             auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
    8626          18 :             auto poLocationGroup = poRootGroup->OpenGroup("location");
    8627           9 :             if (poAttrGeotransform &&
    8628           9 :                 poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
    8629           9 :                 poAttrGeotransform->GetDimensionCount() == 1 &&
    8630          27 :                 poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
    8631           9 :                 poLocationGroup)
    8632             :             {
    8633          18 :                 auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
    8634          18 :                 auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
    8635          27 :                 if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
    8636          18 :                     poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
    8637          18 :                     poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
    8638          27 :                     poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
    8639          27 :                     poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
    8640           9 :                     poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
    8641             :                 {
    8642             :                     return CreateGLTOrthorectified(
    8643             :                         self, poRootGroup, poGLT_X, poGLT_Y,
    8644             :                         /* nGLTIndexOffset = */ -1,
    8645          18 :                         poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
    8646             :                 }
    8647             :             }
    8648             :         }
    8649             :     }
    8650             : 
    8651          29 :     if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
    8652             :                                          "EMIT_ORTHORECTIFICATION", "NO")))
    8653             :     {
    8654           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8655             :                  "EMIT_ORTHORECTIFICATION required, but dataset and/or "
    8656             :                  "parameters are not compatible with it");
    8657           0 :         return nullptr;
    8658             :     }
    8659             : 
    8660             :     return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
    8661          29 :                                         poTargetSRS, papszOptions);
    8662             : }
    8663             : 
    8664             : /************************************************************************/
    8665             : /*                         GDALDatasetFromArray()                       */
    8666             : /************************************************************************/
    8667             : 
    8668             : class GDALDatasetFromArray;
    8669             : 
    8670             : namespace
    8671             : {
    8672             : struct MetadataItem
    8673             : {
    8674             :     std::shared_ptr<GDALAbstractMDArray> poArray{};
    8675             :     std::string osName{};
    8676             :     std::string osDefinition{};
    8677             :     bool bDefinitionUsesPctForG = false;
    8678             : };
    8679             : 
    8680             : struct BandImageryMetadata
    8681             : {
    8682             :     std::shared_ptr<GDALAbstractMDArray> poCentralWavelengthArray{};
    8683             :     double dfCentralWavelengthToMicrometer = 1.0;
    8684             :     std::shared_ptr<GDALAbstractMDArray> poFWHMArray{};
    8685             :     double dfFWHMToMicrometer = 1.0;
    8686             : };
    8687             : 
    8688             : }  // namespace
    8689             : 
    8690             : class GDALRasterBandFromArray final : public GDALPamRasterBand
    8691             : {
    8692             :     std::vector<GUInt64> m_anOffset{};
    8693             :     std::vector<size_t> m_anCount{};
    8694             :     std::vector<GPtrDiff_t> m_anStride{};
    8695             : 
    8696             :   protected:
    8697             :     CPLErr IReadBlock(int, int, void *) override;
    8698             :     CPLErr IWriteBlock(int, int, void *) override;
    8699             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
    8700             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
    8701             :                      GDALDataType eBufType, GSpacing nPixelSpaceBuf,
    8702             :                      GSpacing nLineSpaceBuf,
    8703             :                      GDALRasterIOExtraArg *psExtraArg) override;
    8704             : 
    8705             :   public:
    8706             :     explicit GDALRasterBandFromArray(
    8707             :         GDALDatasetFromArray *poDSIn,
    8708             :         const std::vector<GUInt64> &anOtherDimCoord,
    8709             :         const std::vector<std::vector<MetadataItem>>
    8710             :             &aoBandParameterMetadataItems,
    8711             :         const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
    8712             :         double dfDelay, time_t nStartTime, bool &bHasWarned);
    8713             : 
    8714             :     double GetNoDataValue(int *pbHasNoData) override;
    8715             :     int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
    8716             :     uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
    8717             :     double GetOffset(int *pbHasOffset) override;
    8718             :     double GetScale(int *pbHasScale) override;
    8719             :     const char *GetUnitType() override;
    8720             :     GDALColorInterp GetColorInterpretation() override;
    8721             : };
    8722             : 
    8723             : class GDALDatasetFromArray final : public GDALPamDataset
    8724             : {
    8725             :     friend class GDALRasterBandFromArray;
    8726             : 
    8727             :     std::shared_ptr<GDALMDArray> m_poArray;
    8728             :     size_t m_iXDim;
    8729             :     size_t m_iYDim;
    8730             :     GDALGeoTransform m_gt{};
    8731             :     bool m_bHasGT = false;
    8732             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
    8733             :     GDALMultiDomainMetadata m_oMDD{};
    8734             :     std::string m_osOvrFilename{};
    8735             : 
    8736             :   public:
    8737         240 :     GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
    8738             :                          size_t iXDim, size_t iYDim)
    8739         240 :         : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
    8740             :     {
    8741             :         // Initialize an overview filename from the filename of the array
    8742             :         // and its name.
    8743         240 :         const std::string &osFilename = m_poArray->GetFilename();
    8744         240 :         if (!osFilename.empty())
    8745             :         {
    8746         205 :             m_osOvrFilename = osFilename;
    8747         205 :             m_osOvrFilename += '.';
    8748        8066 :             for (char ch : m_poArray->GetName())
    8749             :             {
    8750        7861 :                 if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
    8751        6956 :                     (ch >= 'a' && ch <= 'z') || ch == '_')
    8752             :                 {
    8753        6290 :                     m_osOvrFilename += ch;
    8754             :                 }
    8755             :                 else
    8756             :                 {
    8757        1571 :                     m_osOvrFilename += '_';
    8758             :                 }
    8759             :             }
    8760         205 :             m_osOvrFilename += ".ovr";
    8761         205 :             oOvManager.Initialize(this);
    8762             :         }
    8763         240 :     }
    8764             : 
    8765             :     static GDALDatasetFromArray *
    8766             :     Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
    8767             :            size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
    8768             :            CSLConstList papszOptions);
    8769             : 
    8770             :     ~GDALDatasetFromArray() override;
    8771             : 
    8772         387 :     CPLErr Close() override
    8773             :     {
    8774         387 :         CPLErr eErr = CE_None;
    8775         387 :         if (nOpenFlags != OPEN_FLAGS_CLOSED)
    8776             :         {
    8777         387 :             if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
    8778             :                 CE_None)
    8779           0 :                 eErr = CE_Failure;
    8780         387 :             m_poArray.reset();
    8781             :         }
    8782         387 :         return eErr;
    8783             :     }
    8784             : 
    8785          73 :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override
    8786             :     {
    8787          73 :         gt = m_gt;
    8788          73 :         return m_bHasGT ? CE_None : CE_Failure;
    8789             :     }
    8790             : 
    8791          70 :     const OGRSpatialReference *GetSpatialRef() const override
    8792             :     {
    8793          70 :         if (m_poArray->GetDimensionCount() < 2)
    8794           3 :             return nullptr;
    8795          67 :         m_poSRS = m_poArray->GetSpatialRef();
    8796          67 :         if (m_poSRS)
    8797             :         {
    8798          28 :             m_poSRS.reset(m_poSRS->Clone());
    8799          56 :             auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
    8800          84 :             for (auto &m : axisMapping)
    8801             :             {
    8802          56 :                 if (m == static_cast<int>(m_iXDim) + 1)
    8803          28 :                     m = 1;
    8804          28 :                 else if (m == static_cast<int>(m_iYDim) + 1)
    8805          28 :                     m = 2;
    8806             :             }
    8807          28 :             m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
    8808             :         }
    8809          67 :         return m_poSRS.get();
    8810             :     }
    8811             : 
    8812           6 :     CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
    8813             :     {
    8814           6 :         return m_oMDD.SetMetadata(papszMetadata, pszDomain);
    8815             :     }
    8816             : 
    8817         179 :     char **GetMetadata(const char *pszDomain) override
    8818             :     {
    8819         179 :         return m_oMDD.GetMetadata(pszDomain);
    8820             :     }
    8821             : 
    8822         237 :     const char *GetMetadataItem(const char *pszName,
    8823             :                                 const char *pszDomain) override
    8824             :     {
    8825         429 :         if (!m_osOvrFilename.empty() && pszName &&
    8826         444 :             EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
    8827          15 :             EQUAL(pszDomain, "OVERVIEWS"))
    8828             :         {
    8829          15 :             return m_osOvrFilename.c_str();
    8830             :         }
    8831         222 :         return m_oMDD.GetMetadataItem(pszName, pszDomain);
    8832             :     }
    8833             : };
    8834             : 
    8835         480 : GDALDatasetFromArray::~GDALDatasetFromArray()
    8836             : {
    8837         240 :     GDALDatasetFromArray::Close();
    8838         480 : }
    8839             : 
    8840             : /************************************************************************/
    8841             : /*                      GDALRasterBandFromArray()                       */
    8842             : /************************************************************************/
    8843             : 
    8844         316 : GDALRasterBandFromArray::GDALRasterBandFromArray(
    8845             :     GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
    8846             :     const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
    8847             :     const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
    8848         316 :     double dfDelay, time_t nStartTime, bool &bHasWarned)
    8849             : {
    8850         316 :     const auto &poArray(poDSIn->m_poArray);
    8851         316 :     const auto &dims(poArray->GetDimensions());
    8852         316 :     const auto nDimCount(dims.size());
    8853         632 :     const auto blockSize(poArray->GetBlockSize());
    8854         300 :     nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
    8855         616 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    8856         150 :                                                   blockSize[poDSIn->m_iYDim]))
    8857             :                       : 1;
    8858         316 :     nBlockXSize = blockSize[poDSIn->m_iXDim]
    8859         165 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    8860         165 :                                                   blockSize[poDSIn->m_iXDim]))
    8861         316 :                       : poDSIn->GetRasterXSize();
    8862         316 :     eDataType = poArray->GetDataType().GetNumericDataType();
    8863         316 :     eAccess = poDSIn->eAccess;
    8864         316 :     m_anOffset.resize(nDimCount);
    8865         316 :     m_anCount.resize(nDimCount, 1);
    8866         316 :     m_anStride.resize(nDimCount);
    8867        1063 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    8868             :     {
    8869         747 :         if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
    8870             :         {
    8871         262 :             std::string dimName(dims[i]->GetName());
    8872         131 :             GUInt64 nIndex = anOtherDimCoord[j];
    8873             :             // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
    8874             :             // subsetted dimensions as generated by GetView()
    8875         131 :             if (STARTS_WITH(dimName.c_str(), "subset_"))
    8876             :             {
    8877             :                 CPLStringList aosTokens(
    8878          12 :                     CSLTokenizeString2(dimName.c_str(), "_", 0));
    8879           6 :                 if (aosTokens.size() == 5)
    8880             :                 {
    8881           6 :                     dimName = aosTokens[1];
    8882          18 :                     const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
    8883           6 :                         aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
    8884           6 :                     const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
    8885           6 :                     nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
    8886           0 :                                           : nStartDim - (nIndex * -nIncrDim);
    8887             :                 }
    8888             :             }
    8889         131 :             if (nDimCount != 3 || dimName != "Band")
    8890             :             {
    8891          70 :                 SetMetadataItem(
    8892             :                     CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
    8893             :                     CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
    8894             :             }
    8895             : 
    8896         131 :             auto indexingVar = dims[i]->GetIndexingVariable();
    8897             : 
    8898             :             // If the indexing variable is also listed in band parameter arrays,
    8899             :             // then don't use our default formatting
    8900         131 :             if (indexingVar)
    8901             :             {
    8902          47 :                 for (const auto &oItem : aoBandParameterMetadataItems[j])
    8903             :                 {
    8904          14 :                     if (oItem.poArray->GetFullName() ==
    8905          14 :                         indexingVar->GetFullName())
    8906             :                     {
    8907          12 :                         indexingVar.reset();
    8908          12 :                         break;
    8909             :                     }
    8910             :                 }
    8911             :             }
    8912             : 
    8913         164 :             if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
    8914          33 :                 indexingVar->GetDimensions()[0]->GetSize() ==
    8915          33 :                     dims[i]->GetSize())
    8916             :             {
    8917          33 :                 if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
    8918             :                 {
    8919           0 :                     if (!bHasWarned)
    8920             :                     {
    8921           0 :                         CPLError(
    8922             :                             CE_Warning, CPLE_AppDefined,
    8923             :                             "Maximum delay to load band metadata from "
    8924             :                             "dimension indexing variables has expired. "
    8925             :                             "Increase the value of the "
    8926             :                             "LOAD_EXTRA_DIM_METADATA_DELAY "
    8927             :                             "option of GDALMDArray::AsClassicDataset() "
    8928             :                             "(also accessible as the "
    8929             :                             "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
    8930             :                             "configuration option), "
    8931             :                             "or set it to 'unlimited' for unlimited delay. ");
    8932           0 :                         bHasWarned = true;
    8933             :                     }
    8934             :                 }
    8935             :                 else
    8936             :                 {
    8937          33 :                     size_t nCount = 1;
    8938          33 :                     const auto &dt(indexingVar->GetDataType());
    8939          66 :                     std::vector<GByte> abyTmp(dt.GetSize());
    8940          66 :                     if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
    8941          33 :                                           nullptr, nullptr, dt, &abyTmp[0]))
    8942             :                     {
    8943          33 :                         char *pszTmp = nullptr;
    8944          33 :                         GDALExtendedDataType::CopyValue(
    8945          33 :                             &abyTmp[0], dt, &pszTmp,
    8946          66 :                             GDALExtendedDataType::CreateString());
    8947          33 :                         dt.FreeDynamicMemory(abyTmp.data());
    8948          33 :                         if (pszTmp)
    8949             :                         {
    8950          33 :                             SetMetadataItem(
    8951             :                                 CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
    8952             :                                 pszTmp);
    8953          33 :                             CPLFree(pszTmp);
    8954             :                         }
    8955             : 
    8956          33 :                         const auto &unit(indexingVar->GetUnit());
    8957          33 :                         if (!unit.empty())
    8958             :                         {
    8959          12 :                             SetMetadataItem(
    8960             :                                 CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
    8961             :                                 unit.c_str());
    8962             :                         }
    8963             :                     }
    8964             :                 }
    8965             :             }
    8966             : 
    8967         149 :             for (const auto &oItem : aoBandParameterMetadataItems[j])
    8968             :             {
    8969          36 :                 CPLString osVal;
    8970             : 
    8971          18 :                 size_t nCount = 1;
    8972          18 :                 const auto &dt(oItem.poArray->GetDataType());
    8973          18 :                 if (oItem.bDefinitionUsesPctForG)
    8974             :                 {
    8975             :                     // There is one and only one %[x][.y]f|g in osDefinition
    8976          16 :                     std::vector<GByte> abyTmp(dt.GetSize());
    8977          16 :                     if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
    8978           8 :                                             nullptr, nullptr, dt, &abyTmp[0]))
    8979             :                     {
    8980           8 :                         double dfVal = 0;
    8981           8 :                         GDALExtendedDataType::CopyValue(
    8982           8 :                             &abyTmp[0], dt, &dfVal,
    8983          16 :                             GDALExtendedDataType::Create(GDT_Float64));
    8984           8 :                         osVal.Printf(oItem.osDefinition.c_str(), dfVal);
    8985           8 :                         dt.FreeDynamicMemory(abyTmp.data());
    8986             :                     }
    8987             :                 }
    8988             :                 else
    8989             :                 {
    8990             :                     // There should be zero or one %s in osDefinition
    8991          10 :                     char *pszValue = nullptr;
    8992          10 :                     if (dt.GetClass() == GEDTC_STRING)
    8993             :                     {
    8994           4 :                         CPL_IGNORE_RET_VAL(oItem.poArray->Read(
    8995           2 :                             &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
    8996           2 :                             dt, &pszValue));
    8997             :                     }
    8998             :                     else
    8999             :                     {
    9000          16 :                         std::vector<GByte> abyTmp(dt.GetSize());
    9001          16 :                         if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
    9002             :                                                 nullptr, nullptr, dt,
    9003           8 :                                                 &abyTmp[0]))
    9004             :                         {
    9005           8 :                             GDALExtendedDataType::CopyValue(
    9006           8 :                                 &abyTmp[0], dt, &pszValue,
    9007          16 :                                 GDALExtendedDataType::CreateString());
    9008             :                         }
    9009             :                     }
    9010             : 
    9011          10 :                     if (pszValue)
    9012             :                     {
    9013          10 :                         osVal.Printf(oItem.osDefinition.c_str(), pszValue);
    9014          10 :                         CPLFree(pszValue);
    9015             :                     }
    9016             :                 }
    9017          18 :                 if (!osVal.empty())
    9018          18 :                     SetMetadataItem(oItem.osName.c_str(), osVal);
    9019             :             }
    9020             : 
    9021         131 :             if (aoBandImageryMetadata[j].poCentralWavelengthArray)
    9022             :             {
    9023             :                 auto &poCentralWavelengthArray =
    9024           4 :                     aoBandImageryMetadata[j].poCentralWavelengthArray;
    9025           4 :                 size_t nCount = 1;
    9026           4 :                 const auto &dt(poCentralWavelengthArray->GetDataType());
    9027           8 :                 std::vector<GByte> abyTmp(dt.GetSize());
    9028           8 :                 if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
    9029             :                                                    &nCount, nullptr, nullptr,
    9030           4 :                                                    dt, &abyTmp[0]))
    9031             :                 {
    9032           4 :                     double dfVal = 0;
    9033           4 :                     GDALExtendedDataType::CopyValue(
    9034           4 :                         &abyTmp[0], dt, &dfVal,
    9035           8 :                         GDALExtendedDataType::Create(GDT_Float64));
    9036           4 :                     dt.FreeDynamicMemory(abyTmp.data());
    9037           4 :                     SetMetadataItem(
    9038             :                         "CENTRAL_WAVELENGTH_UM",
    9039             :                         CPLSPrintf(
    9040           4 :                             "%g", dfVal * aoBandImageryMetadata[j]
    9041           4 :                                               .dfCentralWavelengthToMicrometer),
    9042             :                         "IMAGERY");
    9043             :                 }
    9044             :             }
    9045             : 
    9046         131 :             if (aoBandImageryMetadata[j].poFWHMArray)
    9047             :             {
    9048           2 :                 auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
    9049           2 :                 size_t nCount = 1;
    9050           2 :                 const auto &dt(poFWHMArray->GetDataType());
    9051           4 :                 std::vector<GByte> abyTmp(dt.GetSize());
    9052           4 :                 if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
    9053           2 :                                       nullptr, dt, &abyTmp[0]))
    9054             :                 {
    9055           2 :                     double dfVal = 0;
    9056           2 :                     GDALExtendedDataType::CopyValue(
    9057           2 :                         &abyTmp[0], dt, &dfVal,
    9058           4 :                         GDALExtendedDataType::Create(GDT_Float64));
    9059           2 :                     dt.FreeDynamicMemory(abyTmp.data());
    9060           2 :                     SetMetadataItem(
    9061             :                         "FWHM_UM",
    9062           2 :                         CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
    9063           2 :                                                      .dfFWHMToMicrometer),
    9064             :                         "IMAGERY");
    9065             :                 }
    9066             :             }
    9067             : 
    9068         131 :             m_anOffset[i] = anOtherDimCoord[j];
    9069         131 :             j++;
    9070             :         }
    9071             :     }
    9072         316 : }
    9073             : 
    9074             : /************************************************************************/
    9075             : /*                           GetNoDataValue()                           */
    9076             : /************************************************************************/
    9077             : 
    9078         114 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
    9079             : {
    9080         114 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9081         114 :     const auto &poArray(l_poDS->m_poArray);
    9082         114 :     bool bHasNodata = false;
    9083         114 :     const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
    9084         114 :     if (pbHasNoData)
    9085         102 :         *pbHasNoData = bHasNodata;
    9086         114 :     return res;
    9087             : }
    9088             : 
    9089             : /************************************************************************/
    9090             : /*                       GetNoDataValueAsInt64()                        */
    9091             : /************************************************************************/
    9092             : 
    9093           1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
    9094             : {
    9095           1 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9096           1 :     const auto &poArray(l_poDS->m_poArray);
    9097           1 :     bool bHasNodata = false;
    9098           1 :     const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
    9099           1 :     if (pbHasNoData)
    9100           1 :         *pbHasNoData = bHasNodata;
    9101           1 :     return nodata;
    9102             : }
    9103             : 
    9104             : /************************************************************************/
    9105             : /*                      GetNoDataValueAsUInt64()                        */
    9106             : /************************************************************************/
    9107             : 
    9108           1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
    9109             : {
    9110           1 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9111           1 :     const auto &poArray(l_poDS->m_poArray);
    9112           1 :     bool bHasNodata = false;
    9113           1 :     const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
    9114           1 :     if (pbHasNoData)
    9115           1 :         *pbHasNoData = bHasNodata;
    9116           1 :     return nodata;
    9117             : }
    9118             : 
    9119             : /************************************************************************/
    9120             : /*                             GetOffset()                              */
    9121             : /************************************************************************/
    9122             : 
    9123          40 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
    9124             : {
    9125          40 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9126          40 :     const auto &poArray(l_poDS->m_poArray);
    9127          40 :     bool bHasValue = false;
    9128          40 :     double dfRes = poArray->GetOffset(&bHasValue);
    9129          40 :     if (pbHasOffset)
    9130          21 :         *pbHasOffset = bHasValue;
    9131          40 :     return dfRes;
    9132             : }
    9133             : 
    9134             : /************************************************************************/
    9135             : /*                           GetUnitType()                              */
    9136             : /************************************************************************/
    9137             : 
    9138          46 : const char *GDALRasterBandFromArray::GetUnitType()
    9139             : {
    9140          46 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9141          46 :     const auto &poArray(l_poDS->m_poArray);
    9142          46 :     return poArray->GetUnit().c_str();
    9143             : }
    9144             : 
    9145             : /************************************************************************/
    9146             : /*                             GetScale()                              */
    9147             : /************************************************************************/
    9148             : 
    9149          38 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
    9150             : {
    9151          38 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9152          38 :     const auto &poArray(l_poDS->m_poArray);
    9153          38 :     bool bHasValue = false;
    9154          38 :     double dfRes = poArray->GetScale(&bHasValue);
    9155          38 :     if (pbHasScale)
    9156          19 :         *pbHasScale = bHasValue;
    9157          38 :     return dfRes;
    9158             : }
    9159             : 
    9160             : /************************************************************************/
    9161             : /*                            IReadBlock()                              */
    9162             : /************************************************************************/
    9163             : 
    9164          68 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
    9165             :                                            void *pImage)
    9166             : {
    9167          68 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    9168          68 :     const int nXOff = nBlockXOff * nBlockXSize;
    9169          68 :     const int nYOff = nBlockYOff * nBlockYSize;
    9170          68 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    9171          68 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    9172             :     GDALRasterIOExtraArg sExtraArg;
    9173          68 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    9174         136 :     return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    9175             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    9176         136 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    9177             : }
    9178             : 
    9179             : /************************************************************************/
    9180             : /*                            IWriteBlock()                             */
    9181             : /************************************************************************/
    9182             : 
    9183           1 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
    9184             :                                             void *pImage)
    9185             : {
    9186           1 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    9187           1 :     const int nXOff = nBlockXOff * nBlockXSize;
    9188           1 :     const int nYOff = nBlockYOff * nBlockYSize;
    9189           1 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    9190           1 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    9191             :     GDALRasterIOExtraArg sExtraArg;
    9192           1 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    9193           2 :     return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    9194             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    9195           2 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    9196             : }
    9197             : 
    9198             : /************************************************************************/
    9199             : /*                            IRasterIO()                               */
    9200             : /************************************************************************/
    9201             : 
    9202         360 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
    9203             :                                           int nYOff, int nXSize, int nYSize,
    9204             :                                           void *pData, int nBufXSize,
    9205             :                                           int nBufYSize, GDALDataType eBufType,
    9206             :                                           GSpacing nPixelSpaceBuf,
    9207             :                                           GSpacing nLineSpaceBuf,
    9208             :                                           GDALRasterIOExtraArg *psExtraArg)
    9209             : {
    9210         360 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9211         360 :     const auto &poArray(l_poDS->m_poArray);
    9212         360 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
    9213         360 :     if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
    9214         360 :         (nPixelSpaceBuf % nBufferDTSize) == 0 &&
    9215         360 :         (nLineSpaceBuf % nBufferDTSize) == 0)
    9216             :     {
    9217         360 :         m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
    9218         360 :         m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
    9219         720 :         m_anStride[l_poDS->m_iXDim] =
    9220         360 :             static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
    9221         360 :         if (poArray->GetDimensionCount() >= 2)
    9222             :         {
    9223         347 :             m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
    9224         347 :             m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
    9225         347 :             m_anStride[l_poDS->m_iYDim] =
    9226         347 :                 static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
    9227             :         }
    9228         360 :         if (eRWFlag == GF_Read)
    9229             :         {
    9230         708 :             return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
    9231         354 :                                  m_anStride.data(),
    9232         708 :                                  GDALExtendedDataType::Create(eBufType), pData)
    9233         354 :                        ? CE_None
    9234         354 :                        : CE_Failure;
    9235             :         }
    9236             :         else
    9237             :         {
    9238          12 :             return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
    9239           6 :                                   m_anStride.data(),
    9240          12 :                                   GDALExtendedDataType::Create(eBufType), pData)
    9241           6 :                        ? CE_None
    9242           6 :                        : CE_Failure;
    9243             :         }
    9244             :     }
    9245           0 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    9246             :                                      pData, nBufXSize, nBufYSize, eBufType,
    9247           0 :                                      nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
    9248             : }
    9249             : 
    9250             : /************************************************************************/
    9251             : /*                      GetColorInterpretation()                        */
    9252             : /************************************************************************/
    9253             : 
    9254          66 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
    9255             : {
    9256          66 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9257          66 :     const auto &poArray(l_poDS->m_poArray);
    9258         198 :     auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
    9259          66 :     if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
    9260             :     {
    9261           6 :         bool bOK = false;
    9262           6 :         GUInt64 nStartIndex = 0;
    9263           6 :         if (poArray->GetDimensionCount() == 2 &&
    9264           0 :             poAttr->GetDimensionCount() == 0)
    9265             :         {
    9266           0 :             bOK = true;
    9267             :         }
    9268           6 :         else if (poArray->GetDimensionCount() == 3)
    9269             :         {
    9270           6 :             uint64_t nExtraDimSamples = 1;
    9271           6 :             const auto &apoDims = poArray->GetDimensions();
    9272          24 :             for (size_t i = 0; i < apoDims.size(); ++i)
    9273             :             {
    9274          18 :                 if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
    9275           6 :                     nExtraDimSamples *= apoDims[i]->GetSize();
    9276             :             }
    9277           6 :             if (poAttr->GetDimensionsSize() ==
    9278          12 :                 std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
    9279             :             {
    9280           6 :                 bOK = true;
    9281             :             }
    9282           6 :             nStartIndex = nBand - 1;
    9283             :         }
    9284           6 :         if (bOK)
    9285             :         {
    9286           6 :             const auto oStringDT = GDALExtendedDataType::CreateString();
    9287           6 :             const size_t nCount = 1;
    9288           6 :             const GInt64 arrayStep = 1;
    9289           6 :             const GPtrDiff_t bufferStride = 1;
    9290           6 :             char *pszValue = nullptr;
    9291           6 :             poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
    9292           6 :                          oStringDT, &pszValue);
    9293           6 :             if (pszValue)
    9294             :             {
    9295             :                 const auto eColorInterp =
    9296           6 :                     GDALGetColorInterpretationByName(pszValue);
    9297           6 :                 CPLFree(pszValue);
    9298           6 :                 return eColorInterp;
    9299             :             }
    9300             :         }
    9301             :     }
    9302          60 :     return GCI_Undefined;
    9303             : }
    9304             : 
    9305             : /************************************************************************/
    9306             : /*                    GDALDatasetFromArray::Create()                    */
    9307             : /************************************************************************/
    9308             : 
    9309         292 : GDALDatasetFromArray *GDALDatasetFromArray::Create(
    9310             :     const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
    9311             :     const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
    9312             : 
    9313             : {
    9314         292 :     const auto nDimCount(array->GetDimensionCount());
    9315         292 :     if (nDimCount == 0)
    9316             :     {
    9317           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    9318             :                  "Unsupported number of dimensions");
    9319           1 :         return nullptr;
    9320             :     }
    9321         581 :     if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
    9322         290 :         array->GetDataType().GetNumericDataType() == GDT_Unknown)
    9323             :     {
    9324           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    9325             :                  "Only arrays with numeric data types "
    9326             :                  "can be exposed as classic GDALDataset");
    9327           1 :         return nullptr;
    9328             :     }
    9329         290 :     if (iXDim >= nDimCount || iYDim >= nDimCount ||
    9330         267 :         (nDimCount >= 2 && iXDim == iYDim))
    9331             :     {
    9332           8 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
    9333           8 :         return nullptr;
    9334             :     }
    9335         282 :     GUInt64 nTotalBands = 1;
    9336         282 :     const auto &dims(array->GetDimensions());
    9337         921 :     for (size_t i = 0; i < nDimCount; ++i)
    9338             :     {
    9339         640 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9340             :         {
    9341          94 :             if (dims[i]->GetSize() > 65536 / nTotalBands)
    9342             :             {
    9343           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9344             :                          "Too many bands. Operate on a sliced view");
    9345           1 :                 return nullptr;
    9346             :             }
    9347          93 :             nTotalBands *= dims[i]->GetSize();
    9348             :         }
    9349             :     }
    9350             : 
    9351         562 :     std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
    9352         562 :     std::vector<size_t> oMapArrayExtraDimIdxToOriginalIdx;
    9353         920 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    9354             :     {
    9355         639 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9356             :         {
    9357          93 :             oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
    9358          93 :             oMapArrayExtraDimIdxToOriginalIdx.push_back(i);
    9359          93 :             ++j;
    9360             :         }
    9361             :     }
    9362             : 
    9363         281 :     const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
    9364             : 
    9365             :     const char *pszBandMetadata =
    9366         281 :         CSLFetchNameValue(papszOptions, "BAND_METADATA");
    9367             :     std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
    9368         562 :         nNewDimCount);
    9369         281 :     if (pszBandMetadata)
    9370             :     {
    9371          32 :         if (!poRootGroup)
    9372             :         {
    9373           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9374             :                      "Root group should be provided when BAND_METADATA is set");
    9375          24 :             return nullptr;
    9376             :         }
    9377          31 :         CPLJSONDocument oDoc;
    9378          31 :         if (!oDoc.LoadMemory(pszBandMetadata))
    9379             :         {
    9380           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9381             :                      "Invalid JSON content for BAND_METADATA");
    9382           1 :             return nullptr;
    9383             :         }
    9384          30 :         auto oRoot = oDoc.GetRoot();
    9385          30 :         if (oRoot.GetType() != CPLJSONObject::Type::Array)
    9386             :         {
    9387           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9388             :                      "Value of BAND_METADATA should be an array");
    9389           1 :             return nullptr;
    9390             :         }
    9391             : 
    9392          29 :         auto oArray = oRoot.ToArray();
    9393          38 :         for (int j = 0; j < oArray.Size(); ++j)
    9394             :         {
    9395          30 :             const auto oJsonItem = oArray[j];
    9396          30 :             MetadataItem oItem;
    9397          30 :             size_t iExtraDimIdx = 0;
    9398             : 
    9399          60 :             const auto osBandArrayFullname = oJsonItem.GetString("array");
    9400          60 :             const auto osBandAttributeName = oJsonItem.GetString("attribute");
    9401           0 :             std::shared_ptr<GDALMDArray> poArray;
    9402           0 :             std::shared_ptr<GDALAttribute> poAttribute;
    9403          30 :             if (osBandArrayFullname.empty() && osBandAttributeName.empty())
    9404             :             {
    9405           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9406             :                          "BAND_METADATA[%d][\"array\"] or "
    9407             :                          "BAND_METADATA[%d][\"attribute\"] is missing",
    9408             :                          j, j);
    9409           1 :                 return nullptr;
    9410             :             }
    9411          48 :             else if (!osBandArrayFullname.empty() &&
    9412          19 :                      !osBandAttributeName.empty())
    9413             :             {
    9414           1 :                 CPLError(
    9415             :                     CE_Failure, CPLE_AppDefined,
    9416             :                     "BAND_METADATA[%d][\"array\"] and "
    9417             :                     "BAND_METADATA[%d][\"attribute\"] are mutually exclusive",
    9418             :                     j, j);
    9419           1 :                 return nullptr;
    9420             :             }
    9421          28 :             else if (!osBandArrayFullname.empty())
    9422             :             {
    9423             :                 poArray =
    9424          18 :                     poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
    9425          18 :                 if (!poArray)
    9426             :                 {
    9427           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9428             :                              "Array %s cannot be found",
    9429             :                              osBandArrayFullname.c_str());
    9430           3 :                     return nullptr;
    9431             :                 }
    9432          17 :                 if (poArray->GetDimensionCount() != 1)
    9433             :                 {
    9434           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9435             :                              "Array %s is not a 1D array",
    9436             :                              osBandArrayFullname.c_str());
    9437           1 :                     return nullptr;
    9438             :                 }
    9439             :                 const auto &osAuxArrayDimName =
    9440          16 :                     poArray->GetDimensions()[0]->GetName();
    9441             :                 auto oIter =
    9442          16 :                     oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
    9443          16 :                 if (oIter == oMapArrayDimNameToExtraDimIdx.end())
    9444             :                 {
    9445           1 :                     CPLError(
    9446             :                         CE_Failure, CPLE_AppDefined,
    9447             :                         "Dimension %s of array %s is not a non-X/Y dimension "
    9448             :                         "of array %s",
    9449             :                         osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
    9450           1 :                         array->GetName().c_str());
    9451           1 :                     return nullptr;
    9452             :                 }
    9453          15 :                 iExtraDimIdx = oIter->second;
    9454          15 :                 CPLAssert(iExtraDimIdx < nNewDimCount);
    9455             :             }
    9456             :             else
    9457             :             {
    9458          10 :                 CPLAssert(!osBandAttributeName.empty());
    9459          10 :                 poAttribute = !osBandAttributeName.empty() &&
    9460          10 :                                       osBandAttributeName[0] == '/'
    9461          24 :                                   ? poRootGroup->OpenAttributeFromFullname(
    9462             :                                         osBandAttributeName)
    9463          10 :                                   : array->GetAttribute(osBandAttributeName);
    9464          10 :                 if (!poAttribute)
    9465             :                 {
    9466           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9467             :                              "Attribute %s cannot be found",
    9468             :                              osBandAttributeName.c_str());
    9469           8 :                     return nullptr;
    9470             :                 }
    9471           8 :                 const auto aoAttrDims = poAttribute->GetDimensionsSize();
    9472           8 :                 if (aoAttrDims.size() != 1)
    9473             :                 {
    9474           4 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9475             :                              "Attribute %s is not a 1D array",
    9476             :                              osBandAttributeName.c_str());
    9477           4 :                     return nullptr;
    9478             :                 }
    9479           4 :                 bool found = false;
    9480           8 :                 for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
    9481             :                 {
    9482           5 :                     if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
    9483           5 :                             ->GetSize() == aoAttrDims[0])
    9484             :                     {
    9485           4 :                         if (found)
    9486             :                         {
    9487           2 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9488             :                                      "Several dimensions of %s have the same "
    9489             :                                      "size as attribute %s. Cannot infer which "
    9490             :                                      "one to bind to!",
    9491           1 :                                      array->GetName().c_str(),
    9492             :                                      osBandAttributeName.c_str());
    9493           1 :                             return nullptr;
    9494             :                         }
    9495           3 :                         found = true;
    9496           3 :                         iExtraDimIdx = iter.second;
    9497             :                     }
    9498             :                 }
    9499           3 :                 if (!found)
    9500             :                 {
    9501           2 :                     CPLError(
    9502             :                         CE_Failure, CPLE_AppDefined,
    9503             :                         "No dimension of %s has the same size as attribute %s",
    9504           1 :                         array->GetName().c_str(), osBandAttributeName.c_str());
    9505           1 :                     return nullptr;
    9506             :                 }
    9507             :             }
    9508             : 
    9509          17 :             oItem.osName = oJsonItem.GetString("item_name");
    9510          17 :             if (oItem.osName.empty())
    9511             :             {
    9512           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9513             :                          "BAND_METADATA[%d][\"item_name\"] is missing", j);
    9514           1 :                 return nullptr;
    9515             :             }
    9516             : 
    9517          32 :             const auto osDefinition = oJsonItem.GetString("item_value", "%s");
    9518             : 
    9519             :             // Check correctness of definition
    9520          16 :             bool bFirstNumericFormatter = true;
    9521          16 :             std::string osModDefinition;
    9522          16 :             bool bDefinitionUsesPctForG = false;
    9523          79 :             for (size_t k = 0; k < osDefinition.size(); ++k)
    9524             :             {
    9525          70 :                 if (osDefinition[k] == '%')
    9526             :                 {
    9527          15 :                     osModDefinition += osDefinition[k];
    9528          15 :                     if (k + 1 == osDefinition.size())
    9529             :                     {
    9530           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9531             :                                  "Value of "
    9532             :                                  "BAND_METADATA[%d][\"item_value\"] = "
    9533             :                                  "%s is invalid at offset %d",
    9534             :                                  j, osDefinition.c_str(), int(k));
    9535           1 :                         return nullptr;
    9536             :                     }
    9537          14 :                     ++k;
    9538          14 :                     if (osDefinition[k] == '%')
    9539             :                     {
    9540           1 :                         osModDefinition += osDefinition[k];
    9541           1 :                         continue;
    9542             :                     }
    9543          13 :                     if (!bFirstNumericFormatter)
    9544             :                     {
    9545           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9546             :                                  "Value of "
    9547             :                                  "BAND_METADATA[%d][\"item_value\"] = %s is "
    9548             :                                  "invalid at offset %d: %%[x][.y]f|g or %%s "
    9549             :                                  "formatters should be specified at most once",
    9550             :                                  j, osDefinition.c_str(), int(k));
    9551           1 :                         return nullptr;
    9552             :                     }
    9553          12 :                     bFirstNumericFormatter = false;
    9554          19 :                     for (; k < osDefinition.size(); ++k)
    9555             :                     {
    9556          19 :                         osModDefinition += osDefinition[k];
    9557          38 :                         if (!((osDefinition[k] >= '0' &&
    9558          16 :                                osDefinition[k] <= '9') ||
    9559          15 :                               osDefinition[k] == '.'))
    9560          12 :                             break;
    9561             :                     }
    9562          24 :                     if (k == osDefinition.size() ||
    9563          12 :                         (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
    9564           5 :                          osDefinition[k] != 's'))
    9565             :                     {
    9566           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9567             :                                  "Value of "
    9568             :                                  "BAND_METADATA[%d][\"item_value\"] = "
    9569             :                                  "%s is invalid at offset %d: only "
    9570             :                                  "%%[x][.y]f|g or %%s formatters are accepted",
    9571             :                                  j, osDefinition.c_str(), int(k));
    9572           1 :                         return nullptr;
    9573             :                     }
    9574          11 :                     bDefinitionUsesPctForG =
    9575          11 :                         (osDefinition[k] == 'f' || osDefinition[k] == 'g');
    9576          11 :                     if (bDefinitionUsesPctForG)
    9577             :                     {
    9578          12 :                         if (poArray &&
    9579          12 :                             poArray->GetDataType().GetClass() != GEDTC_NUMERIC)
    9580             :                         {
    9581           1 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9582             :                                      "Data type of %s array is not numeric",
    9583           1 :                                      poArray->GetName().c_str());
    9584           1 :                             return nullptr;
    9585             :                         }
    9586           8 :                         else if (poAttribute &&
    9587           2 :                                  poAttribute->GetDataType().GetClass() !=
    9588           6 :                                      GEDTC_NUMERIC)
    9589             :                         {
    9590           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9591             :                                      "Data type of %s attribute is not numeric",
    9592           0 :                                      poAttribute->GetFullName().c_str());
    9593           0 :                             return nullptr;
    9594             :                         }
    9595             :                     }
    9596             :                 }
    9597          62 :                 else if (osDefinition[k] == '$' &&
    9598          62 :                          k + 1 < osDefinition.size() &&
    9599           7 :                          osDefinition[k + 1] == '{')
    9600             :                 {
    9601           7 :                     const auto nPos = osDefinition.find('}', k);
    9602           7 :                     if (nPos == std::string::npos)
    9603             :                     {
    9604           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9605             :                                  "Value of "
    9606             :                                  "BAND_METADATA[%d][\"item_value\"] = "
    9607             :                                  "%s is invalid at offset %d",
    9608             :                                  j, osDefinition.c_str(), int(k));
    9609           3 :                         return nullptr;
    9610             :                     }
    9611             :                     const auto osAttrName =
    9612           6 :                         osDefinition.substr(k + 2, nPos - (k + 2));
    9613           0 :                     std::shared_ptr<GDALAttribute> poAttr;
    9614           6 :                     if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
    9615             :                     {
    9616           4 :                         poAttr = poArray->GetAttribute(osAttrName);
    9617           4 :                         if (!poAttr)
    9618             :                         {
    9619           1 :                             CPLError(
    9620             :                                 CE_Failure, CPLE_AppDefined,
    9621             :                                 "Value of "
    9622             :                                 "BAND_METADATA[%d][\"item_value\"] = "
    9623             :                                 "%s is invalid: %s is not an attribute of %s",
    9624             :                                 j, osDefinition.c_str(), osAttrName.c_str(),
    9625           1 :                                 poArray->GetName().c_str());
    9626           1 :                             return nullptr;
    9627             :                         }
    9628             :                     }
    9629             :                     else
    9630             :                     {
    9631             :                         poAttr =
    9632           2 :                             poRootGroup->OpenAttributeFromFullname(osAttrName);
    9633           2 :                         if (!poAttr)
    9634             :                         {
    9635           1 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9636             :                                      "Value of "
    9637             :                                      "BAND_METADATA[%d][\"item_value\"] = "
    9638             :                                      "%s is invalid: %s is not an attribute",
    9639             :                                      j, osDefinition.c_str(),
    9640             :                                      osAttrName.c_str());
    9641           1 :                             return nullptr;
    9642             :                         }
    9643             :                     }
    9644           4 :                     k = nPos;
    9645           4 :                     const char *pszValue = poAttr->ReadAsString();
    9646           4 :                     if (!pszValue)
    9647             :                     {
    9648           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9649             :                                  "Cannot get value of attribute %s as a "
    9650             :                                  "string",
    9651             :                                  osAttrName.c_str());
    9652           0 :                         return nullptr;
    9653             :                     }
    9654           4 :                     osModDefinition += pszValue;
    9655             :                 }
    9656             :                 else
    9657             :                 {
    9658          48 :                     osModDefinition += osDefinition[k];
    9659             :                 }
    9660             :             }
    9661             : 
    9662           9 :             if (poArray)
    9663           8 :                 oItem.poArray = std::move(poArray);
    9664             :             else
    9665           1 :                 oItem.poArray = std::move(poAttribute);
    9666           9 :             oItem.osDefinition = std::move(osModDefinition);
    9667           9 :             oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
    9668             : 
    9669           9 :             aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
    9670           9 :                 std::move(oItem));
    9671             :         }
    9672             :     }
    9673             : 
    9674         514 :     std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
    9675             :     const char *pszBandImageryMetadata =
    9676         257 :         CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
    9677         257 :     if (pszBandImageryMetadata)
    9678             :     {
    9679          20 :         if (!poRootGroup)
    9680             :         {
    9681           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9682             :                      "Root group should be provided when BAND_IMAGERY_METADATA "
    9683             :                      "is set");
    9684          17 :             return nullptr;
    9685             :         }
    9686          19 :         CPLJSONDocument oDoc;
    9687          19 :         if (!oDoc.LoadMemory(pszBandImageryMetadata))
    9688             :         {
    9689           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9690             :                      "Invalid JSON content for BAND_IMAGERY_METADATA");
    9691           1 :             return nullptr;
    9692             :         }
    9693          18 :         auto oRoot = oDoc.GetRoot();
    9694          18 :         if (oRoot.GetType() != CPLJSONObject::Type::Object)
    9695             :         {
    9696           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9697             :                      "Value of BAND_IMAGERY_METADATA should be an object");
    9698           1 :             return nullptr;
    9699             :         }
    9700          21 :         for (const auto &oJsonItem : oRoot.GetChildren())
    9701             :         {
    9702          38 :             if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
    9703          20 :                 oJsonItem.GetName() == "FWHM_UM")
    9704             :             {
    9705          34 :                 const auto osBandArrayFullname = oJsonItem.GetString("array");
    9706             :                 const auto osBandAttributeName =
    9707          34 :                     oJsonItem.GetString("attribute");
    9708           0 :                 std::shared_ptr<GDALMDArray> poArray;
    9709           0 :                 std::shared_ptr<GDALAttribute> poAttribute;
    9710          17 :                 size_t iExtraDimIdx = 0;
    9711          17 :                 if (osBandArrayFullname.empty() && osBandAttributeName.empty())
    9712             :                 {
    9713           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9714             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] or "
    9715             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] is "
    9716             :                              "missing",
    9717           2 :                              oJsonItem.GetName().c_str(),
    9718           2 :                              oJsonItem.GetName().c_str());
    9719           1 :                     return nullptr;
    9720             :                 }
    9721          25 :                 else if (!osBandArrayFullname.empty() &&
    9722           9 :                          !osBandAttributeName.empty())
    9723             :                 {
    9724           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9725             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] and "
    9726             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"attribute\"] are "
    9727             :                              "mutually exclusive",
    9728           2 :                              oJsonItem.GetName().c_str(),
    9729           2 :                              oJsonItem.GetName().c_str());
    9730           1 :                     return nullptr;
    9731             :                 }
    9732          15 :                 else if (!osBandArrayFullname.empty())
    9733             :                 {
    9734          16 :                     poArray = poRootGroup->OpenMDArrayFromFullname(
    9735           8 :                         osBandArrayFullname);
    9736           8 :                     if (!poArray)
    9737             :                     {
    9738           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9739             :                                  "Array %s cannot be found",
    9740             :                                  osBandArrayFullname.c_str());
    9741           3 :                         return nullptr;
    9742             :                     }
    9743           7 :                     if (poArray->GetDimensionCount() != 1)
    9744             :                     {
    9745           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9746             :                                  "Array %s is not a 1D array",
    9747             :                                  osBandArrayFullname.c_str());
    9748           1 :                         return nullptr;
    9749             :                     }
    9750             :                     const auto &osAuxArrayDimName =
    9751           6 :                         poArray->GetDimensions()[0]->GetName();
    9752             :                     auto oIter =
    9753           6 :                         oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
    9754           6 :                     if (oIter == oMapArrayDimNameToExtraDimIdx.end())
    9755             :                     {
    9756           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9757             :                                  "Dimension \"%s\" of array \"%s\" is not a "
    9758             :                                  "non-X/Y dimension of array \"%s\"",
    9759             :                                  osAuxArrayDimName.c_str(),
    9760             :                                  osBandArrayFullname.c_str(),
    9761           1 :                                  array->GetName().c_str());
    9762           1 :                         return nullptr;
    9763             :                     }
    9764           5 :                     iExtraDimIdx = oIter->second;
    9765           5 :                     CPLAssert(iExtraDimIdx < nNewDimCount);
    9766             :                 }
    9767             :                 else
    9768             :                 {
    9769             :                     poAttribute =
    9770           7 :                         !osBandAttributeName.empty() &&
    9771           7 :                                 osBandAttributeName[0] == '/'
    9772          16 :                             ? poRootGroup->OpenAttributeFromFullname(
    9773             :                                   osBandAttributeName)
    9774           7 :                             : array->GetAttribute(osBandAttributeName);
    9775           7 :                     if (!poAttribute)
    9776             :                     {
    9777           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9778             :                                  "Attribute %s cannot be found",
    9779             :                                  osBandAttributeName.c_str());
    9780           6 :                         return nullptr;
    9781             :                     }
    9782           5 :                     const auto aoAttrDims = poAttribute->GetDimensionsSize();
    9783           5 :                     if (aoAttrDims.size() != 1)
    9784             :                     {
    9785           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9786             :                                  "Attribute %s is not a 1D array",
    9787             :                                  osBandAttributeName.c_str());
    9788           2 :                         return nullptr;
    9789             :                     }
    9790           3 :                     bool found = false;
    9791           6 :                     for (const auto &iter : oMapArrayDimNameToExtraDimIdx)
    9792             :                     {
    9793           4 :                         if (dims[oMapArrayExtraDimIdxToOriginalIdx[iter.second]]
    9794           4 :                                 ->GetSize() == aoAttrDims[0])
    9795             :                         {
    9796           3 :                             if (found)
    9797             :                             {
    9798           2 :                                 CPLError(CE_Failure, CPLE_AppDefined,
    9799             :                                          "Several dimensions of %s have the "
    9800             :                                          "same size as attribute %s. Cannot "
    9801             :                                          "infer which one to bind to!",
    9802           1 :                                          array->GetName().c_str(),
    9803             :                                          osBandAttributeName.c_str());
    9804           1 :                                 return nullptr;
    9805             :                             }
    9806           2 :                             found = true;
    9807           2 :                             iExtraDimIdx = iter.second;
    9808             :                         }
    9809             :                     }
    9810           2 :                     if (!found)
    9811             :                     {
    9812           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9813             :                                  "No dimension of %s has the same size as "
    9814             :                                  "attribute %s",
    9815           1 :                                  array->GetName().c_str(),
    9816             :                                  osBandAttributeName.c_str());
    9817           1 :                         return nullptr;
    9818             :                     }
    9819             :                 }
    9820             : 
    9821          12 :                 std::string osUnit = oJsonItem.GetString("unit", "um");
    9822           6 :                 if (STARTS_WITH(osUnit.c_str(), "${"))
    9823             :                 {
    9824           4 :                     if (osUnit.back() != '}')
    9825             :                     {
    9826           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9827             :                                  "Value of "
    9828             :                                  "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
    9829             :                                  "%s is invalid",
    9830           2 :                                  oJsonItem.GetName().c_str(), osUnit.c_str());
    9831           2 :                         return nullptr;
    9832             :                     }
    9833           3 :                     const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
    9834           0 :                     std::shared_ptr<GDALAttribute> poAttr;
    9835           3 :                     if (poArray && !osAttrName.empty() && osAttrName[0] != '/')
    9836             :                     {
    9837           2 :                         poAttr = poArray->GetAttribute(osAttrName);
    9838           2 :                         if (!poAttr)
    9839             :                         {
    9840           2 :                             CPLError(
    9841             :                                 CE_Failure, CPLE_AppDefined,
    9842             :                                 "Value of "
    9843             :                                 "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
    9844             :                                 "%s is invalid: %s is not an attribute of %s",
    9845           2 :                                 oJsonItem.GetName().c_str(), osUnit.c_str(),
    9846             :                                 osAttrName.c_str(),
    9847             :                                 osBandArrayFullname.c_str());
    9848           1 :                             return nullptr;
    9849             :                         }
    9850             :                     }
    9851             :                     else
    9852             :                     {
    9853             :                         poAttr =
    9854           1 :                             poRootGroup->OpenAttributeFromFullname(osAttrName);
    9855           1 :                         if (!poAttr)
    9856             :                         {
    9857           0 :                             CPLError(
    9858             :                                 CE_Failure, CPLE_AppDefined,
    9859             :                                 "Value of "
    9860             :                                 "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
    9861             :                                 "%s is invalid: %s is not an attribute",
    9862           0 :                                 oJsonItem.GetName().c_str(), osUnit.c_str(),
    9863             :                                 osAttrName.c_str());
    9864           0 :                             return nullptr;
    9865             :                         }
    9866             :                     }
    9867             : 
    9868           2 :                     const char *pszValue = poAttr->ReadAsString();
    9869           2 :                     if (!pszValue)
    9870             :                     {
    9871           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9872             :                                  "Cannot get value of attribute %s of %s as a "
    9873             :                                  "string",
    9874             :                                  osAttrName.c_str(),
    9875             :                                  osBandArrayFullname.c_str());
    9876           0 :                         return nullptr;
    9877             :                     }
    9878           2 :                     osUnit = pszValue;
    9879             :                 }
    9880           4 :                 double dfConvToUM = 1.0;
    9881          10 :                 if (osUnit == "nm" || osUnit == "nanometre" ||
    9882          13 :                     osUnit == "nanometres" || osUnit == "nanometer" ||
    9883           3 :                     osUnit == "nanometers")
    9884             :                 {
    9885           1 :                     dfConvToUM = 1e-3;
    9886             :                 }
    9887           5 :                 else if (!(osUnit == "um" || osUnit == "micrometre" ||
    9888           2 :                            osUnit == "micrometres" || osUnit == "micrometer" ||
    9889           1 :                            osUnit == "micrometers"))
    9890             :                 {
    9891           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9892             :                              "Unhandled value for "
    9893             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
    9894           2 :                              oJsonItem.GetName().c_str(), osUnit.c_str());
    9895           1 :                     return nullptr;
    9896             :                 }
    9897             : 
    9898           3 :                 BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
    9899             : 
    9900           3 :                 std::shared_ptr<GDALAbstractMDArray> abstractArray;
    9901           3 :                 if (poArray)
    9902           2 :                     abstractArray = std::move(poArray);
    9903             :                 else
    9904           1 :                     abstractArray = std::move(poAttribute);
    9905           3 :                 if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
    9906             :                 {
    9907           2 :                     item.poCentralWavelengthArray = std::move(abstractArray);
    9908           2 :                     item.dfCentralWavelengthToMicrometer = dfConvToUM;
    9909             :                 }
    9910             :                 else
    9911             :                 {
    9912           1 :                     item.poFWHMArray = std::move(abstractArray);
    9913           1 :                     item.dfFWHMToMicrometer = dfConvToUM;
    9914             :                 }
    9915             :             }
    9916             :             else
    9917             :             {
    9918           1 :                 CPLError(CE_Warning, CPLE_AppDefined,
    9919             :                          "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
    9920           2 :                          oJsonItem.GetName().c_str());
    9921             :             }
    9922             :         }
    9923             :     }
    9924             : 
    9925         480 :     auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
    9926             : 
    9927         240 :     poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
    9928             : 
    9929         240 :     poDS->nRasterYSize =
    9930         240 :         nDimCount < 2 ? 1
    9931         224 :                       : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    9932         224 :                                                   dims[iYDim]->GetSize()));
    9933         480 :     poDS->nRasterXSize = static_cast<int>(
    9934         240 :         std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
    9935             : 
    9936         480 :     std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
    9937         480 :     std::vector<GUInt64> anStackIters(nDimCount);
    9938         480 :     std::vector<size_t> anMapNewToOld(nNewDimCount);
    9939         754 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    9940             :     {
    9941         514 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9942             :         {
    9943          50 :             anMapNewToOld[j] = i;
    9944          50 :             j++;
    9945             :         }
    9946             :     }
    9947             : 
    9948         240 :     poDS->m_bHasGT = array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_gt);
    9949             : 
    9950         480 :     const auto attrs(array->GetAttributes());
    9951         381 :     for (const auto &attr : attrs)
    9952             :     {
    9953         141 :         if (attr->GetName() != "COLOR_INTERPRETATION")
    9954             :         {
    9955         260 :             auto stringArray = attr->ReadAsStringArray();
    9956         260 :             std::string val;
    9957         130 :             if (stringArray.size() > 1)
    9958             :             {
    9959          55 :                 val += '{';
    9960             :             }
    9961         537 :             for (int i = 0; i < stringArray.size(); ++i)
    9962             :             {
    9963         407 :                 if (i > 0)
    9964         277 :                     val += ',';
    9965         407 :                 val += stringArray[i];
    9966             :             }
    9967         130 :             if (stringArray.size() > 1)
    9968             :             {
    9969          55 :                 val += '}';
    9970             :             }
    9971         130 :             poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
    9972             :         }
    9973             :     }
    9974             : 
    9975         240 :     const char *pszDelay = CSLFetchNameValueDef(
    9976             :         papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
    9977             :         CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
    9978             :     const double dfDelay =
    9979         240 :         EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
    9980         240 :     const auto nStartTime = time(nullptr);
    9981         240 :     bool bHasWarned = false;
    9982             :     // Instantiate bands by iterating over non-XY variables
    9983         240 :     size_t iDim = 0;
    9984         240 :     int nCurBand = 1;
    9985         368 : lbl_next_depth:
    9986         368 :     if (iDim < nNewDimCount)
    9987             :     {
    9988          52 :         anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
    9989          52 :         anOtherDimCoord[iDim] = 0;
    9990             :         while (true)
    9991             :         {
    9992         128 :             ++iDim;
    9993         128 :             goto lbl_next_depth;
    9994         128 :         lbl_return_to_caller:
    9995         128 :             --iDim;
    9996         128 :             --anStackIters[iDim];
    9997         128 :             if (anStackIters[iDim] == 0)
    9998          52 :                 break;
    9999          76 :             ++anOtherDimCoord[iDim];
   10000             :         }
   10001             :     }
   10002             :     else
   10003             :     {
   10004         632 :         poDS->SetBand(nCurBand,
   10005             :                       new GDALRasterBandFromArray(
   10006         316 :                           poDS.get(), anOtherDimCoord,
   10007             :                           aoBandParameterMetadataItems, aoBandImageryMetadata,
   10008         316 :                           dfDelay, nStartTime, bHasWarned));
   10009         316 :         ++nCurBand;
   10010             :     }
   10011         368 :     if (iDim > 0)
   10012         128 :         goto lbl_return_to_caller;
   10013             : 
   10014         240 :     if (!array->GetFilename().empty())
   10015             :     {
   10016         205 :         poDS->SetPhysicalFilename(array->GetFilename().c_str());
   10017             :         std::string osDerivedDatasetName(
   10018             :             CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
   10019         410 :                        int(iYDim), array->GetFullName().c_str()));
   10020         205 :         if (!array->GetContext().empty())
   10021             :         {
   10022           2 :             osDerivedDatasetName += " with context ";
   10023           2 :             osDerivedDatasetName += array->GetContext();
   10024             :         }
   10025         205 :         poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
   10026         205 :         poDS->TryLoadXML();
   10027             : 
   10028           2 :         for (const auto &[pszKey, pszValue] :
   10029             :              cpl::IterateNameValue(static_cast<CSLConstList>(
   10030         207 :                  poDS->GDALPamDataset::GetMetadata())))
   10031             :         {
   10032           1 :             poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
   10033             :         }
   10034             :     }
   10035             : 
   10036         240 :     return poDS.release();
   10037             : }
   10038             : 
   10039             : /************************************************************************/
   10040             : /*                          AsClassicDataset()                         */
   10041             : /************************************************************************/
   10042             : 
   10043             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   10044             :  *
   10045             :  * In the case of > 2D arrays, additional dimensions will be represented as
   10046             :  * raster bands.
   10047             :  *
   10048             :  * The "reverse" method is GDALRasterBand::AsMDArray().
   10049             :  *
   10050             :  * This is the same as the C function GDALMDArrayAsClassicDataset().
   10051             :  *
   10052             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   10053             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   10054             :  *              Ignored if the dimension count is 1.
   10055             :  * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
   10056             :  *                    and BAND_IMAGERY_METADATA option.
   10057             :  * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
   10058             :  *                     nullptr. Current supported options are:
   10059             :  *                     <ul>
   10060             :  *                     <li>BAND_METADATA: JSON serialized array defining which
   10061             :  *                         arrays of the poRootGroup, indexed by non-X and Y
   10062             :  *                         dimensions, should be mapped as band metadata items.
   10063             :  *                         Each array item should be an object with the
   10064             :  *                         following members:
   10065             :  *                         - "array": full name of a band parameter array.
   10066             :  *                           Such array must be a one
   10067             :  *                           dimensional array, and its dimension must be one of
   10068             :  *                           the dimensions of the array on which the method is
   10069             :  *                           called (excluding the X and Y dimensions).
   10070             :  *                         - "attribute": name relative to *this array or full
   10071             :  *                           name of a single dimension numeric array whose size
   10072             :  *                           must be one of the dimensions of *this array
   10073             :  *                           (excluding the X and Y dimensions).
   10074             :  *                           "array" and "attribute" are mutually exclusive.
   10075             :  *                         - "item_name": band metadata item name
   10076             :  *                         - "item_value": (optional) String, where "%[x][.y]f",
   10077             :  *                           "%[x][.y]g" or "%s" printf-like formatting can be
   10078             :  *                           used to format the corresponding value of the
   10079             :  *                           parameter array. The percentage character should be
   10080             :  *                           repeated: "%%"
   10081             :  *                           "${attribute_name}" can also be used to include the
   10082             :  *                           value of an attribute for "array" when set and if
   10083             :  *                           not starting with '/'. Otherwise if starting with
   10084             :  *                           '/', it is the full path to the attribute.
   10085             :  *
   10086             :  *                           If "item_value" is not provided, a default formatting
   10087             :  *                           of the value will be applied.
   10088             :  *
   10089             :  *                         Example:
   10090             :  *                         [
   10091             :  *                            {
   10092             :  *                              "array": "/sensor_band_parameters/wavelengths",
   10093             :  *                              "item_name": "WAVELENGTH",
   10094             :  *                              "item_value": "%.1f ${units}"
   10095             :  *                            },
   10096             :  *                            {
   10097             :  *                              "array": "/sensor_band_parameters/fwhm",
   10098             :  *                              "item_name": "FWHM"
   10099             :  *                            },
   10100             :  *                            {
   10101             :  *                              "array": "/sensor_band_parameters/fwhm",
   10102             :  *                              "item_name": "FWHM_UNIT",
   10103             :  *                              "item_value": "${units}"
   10104             :  *                            }
   10105             :  *                         ]
   10106             :  *
   10107             :  *                         Example for Planet Labs Tanager radiance products:
   10108             :  *                         [
   10109             :  *                            {
   10110             :  *                              "attribute": "center_wavelengths",
   10111             :  *                              "item_name": "WAVELENGTH",
   10112             :  *                              "item_value": "%.1f ${center_wavelengths_units}"
   10113             :  *                            }
   10114             :  *                         ]
   10115             :  *
   10116             :  *                     </li>
   10117             :  *                     <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
   10118             :  *                         JSON serialized object defining which arrays of the
   10119             :  *                         poRootGroup, indexed by non-X and Y dimensions,
   10120             :  *                         should be mapped as band metadata items in the
   10121             :  *                         band IMAGERY domain.
   10122             :  *                         The object currently accepts 2 members:
   10123             :  *                         - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
   10124             :  *                           micrometers.
   10125             :  *                         - "FWHM_UM": Full-width half-maximum
   10126             :  *                           in micrometers.
   10127             :  *                         The value of each member should be an object with the
   10128             :  *                         following members:
   10129             :  *                         - "array": full name of a band parameter array.
   10130             :  *                           Such array must be a one dimensional array, and its
   10131             :  *                           dimension must be one of the dimensions of the
   10132             :  *                           array on which the method is called
   10133             :  *                           (excluding the X and Y dimensions).
   10134             :  *                         - "attribute": name relative to *this array or full
   10135             :  *                           name of a single dimension numeric array whose size
   10136             :  *                           must be one of the dimensions of *this array
   10137             :  *                           (excluding the X and Y dimensions).
   10138             :  *                           "array" and "attribute" are mutually exclusive,
   10139             :  *                           and one of them is required.
   10140             :  *                         - "unit": (optional) unit of the values pointed in
   10141             :  *                           the above array.
   10142             :  *                           Can be a literal string or a string of the form
   10143             :  *                           "${attribute_name}" to point to an attribute for
   10144             :  *                           "array" when set and if no starting
   10145             :  *                           with '/'. Otherwise if starting with '/', it is
   10146             :  *                           the full path to the attribute.
   10147             :  *                           Accepted values are "um", "micrometer"
   10148             :  *                           (with UK vs US spelling, singular or plural), "nm",
   10149             :  *                           "nanometer" (with UK vs US spelling, singular or
   10150             :  *                           plural)
   10151             :  *                           If not provided, micrometer is assumed.
   10152             :  *
   10153             :  *                         Example for EMIT datasets:
   10154             :  *                         {
   10155             :  *                            "CENTRAL_WAVELENGTH_UM": {
   10156             :  *                                "array": "/sensor_band_parameters/wavelengths",
   10157             :  *                                "unit": "${units}"
   10158             :  *                            },
   10159             :  *                            "FWHM_UM": {
   10160             :  *                                "array": "/sensor_band_parameters/fwhm",
   10161             :  *                                "unit": "${units}"
   10162             :  *                            }
   10163             :  *                         }
   10164             :  *
   10165             :  *                         Example for Planet Labs Tanager radiance products:
   10166             :  *                         {
   10167             :  *                            "CENTRAL_WAVELENGTH_UM": {
   10168             :  *                              "attribute": "center_wavelengths",
   10169             :  *                              "unit": "${center_wavelengths_units}"
   10170             :  *                            },
   10171             :  *                            "FWHM_UM": {
   10172             :  *                              "attribute": "fwhm",
   10173             :  *                              "unit": "${fwhm_units}"
   10174             :  *                            }
   10175             :  *                         }
   10176             :  *
   10177             :  *                     </li>
   10178             :  *                     <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
   10179             :  *                         seconds allowed to set the DIM_{dimname}_VALUE band
   10180             :  *                         metadata items from the indexing variable of the
   10181             :  *                         dimensions.
   10182             :  *                         Default value is 5. 'unlimited' can be used to mean
   10183             :  *                         unlimited delay. Can also be defined globally with
   10184             :  *                         the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
   10185             :  *                         option.</li>
   10186             :  *                     </ul>
   10187             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   10188             :  */
   10189             : GDALDataset *
   10190         292 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
   10191             :                               const std::shared_ptr<GDALGroup> &poRootGroup,
   10192             :                               CSLConstList papszOptions) const
   10193             : {
   10194         584 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
   10195         292 :     if (!self)
   10196             :     {
   10197           0 :         CPLError(CE_Failure, CPLE_AppDefined,
   10198             :                  "Driver implementation issue: m_pSelf not set !");
   10199           0 :         return nullptr;
   10200             :     }
   10201         292 :     return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
   10202         292 :                                         papszOptions);
   10203             : }
   10204             : 
   10205             : /************************************************************************/
   10206             : /*                           GetStatistics()                            */
   10207             : /************************************************************************/
   10208             : 
   10209             : /**
   10210             :  * \brief Fetch statistics.
   10211             :  *
   10212             :  * Returns the minimum, maximum, mean and standard deviation of all
   10213             :  * pixel values in this array.
   10214             :  *
   10215             :  * If bForce is FALSE results will only be returned if it can be done
   10216             :  * quickly (i.e. without scanning the data).  If bForce is FALSE and
   10217             :  * results cannot be returned efficiently, the method will return CE_Warning
   10218             :  * but no warning will have been issued.   This is a non-standard use of
   10219             :  * the CE_Warning return value to indicate "nothing done".
   10220             :  *
   10221             :  * When cached statistics are not available, and bForce is TRUE,
   10222             :  * ComputeStatistics() is called.
   10223             :  *
   10224             :  * Note that file formats using PAM (Persistent Auxiliary Metadata) services
   10225             :  * will generally cache statistics in the .aux.xml file allowing fast fetch
   10226             :  * after the first request.
   10227             :  *
   10228             :  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
   10229             :  *
   10230             :  * This method is the same as the C function GDALMDArrayGetStatistics().
   10231             :  *
   10232             :  * @param bApproxOK Currently ignored. In the future, should be set to true
   10233             :  * if statistics on the whole array are wished, or to false if a subset of it
   10234             :  * may be used.
   10235             :  *
   10236             :  * @param bForce If false statistics will only be returned if it can
   10237             :  * be done without rescanning the image.
   10238             :  *
   10239             :  * @param pdfMin Location into which to load image minimum (may be NULL).
   10240             :  *
   10241             :  * @param pdfMax Location into which to load image maximum (may be NULL).-
   10242             :  *
   10243             :  * @param pdfMean Location into which to load image mean (may be NULL).
   10244             :  *
   10245             :  * @param pdfStdDev Location into which to load image standard deviation
   10246             :  * (may be NULL).
   10247             :  *
   10248             :  * @param pnValidCount Number of samples whose value is different from the
   10249             :  * nodata value. (may be NULL)
   10250             :  *
   10251             :  * @param pfnProgress a function to call to report progress, or NULL.
   10252             :  *
   10253             :  * @param pProgressData application data to pass to the progress function.
   10254             :  *
   10255             :  * @return CE_None on success, CE_Warning if no values returned,
   10256             :  * CE_Failure if an error occurs.
   10257             :  *
   10258             :  * @since GDAL 3.2
   10259             :  */
   10260             : 
   10261          10 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
   10262             :                                   double *pdfMax, double *pdfMean,
   10263             :                                   double *pdfStdDev, GUInt64 *pnValidCount,
   10264             :                                   GDALProgressFunc pfnProgress,
   10265             :                                   void *pProgressData)
   10266             : {
   10267          10 :     if (!bForce)
   10268           1 :         return CE_Warning;
   10269             : 
   10270          18 :     return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
   10271           9 :                              pnValidCount, pfnProgress, pProgressData, nullptr)
   10272           9 :                ? CE_None
   10273           9 :                : CE_Failure;
   10274             : }
   10275             : 
   10276             : /************************************************************************/
   10277             : /*                         ComputeStatistics()                          */
   10278             : /************************************************************************/
   10279             : 
   10280             : /**
   10281             :  * \brief Compute statistics.
   10282             :  *
   10283             :  * Returns the minimum, maximum, mean and standard deviation of all
   10284             :  * pixel values in this array.
   10285             :  *
   10286             :  * Pixels taken into account in statistics are those whose mask value
   10287             :  * (as determined by GetMask()) is non-zero.
   10288             :  *
   10289             :  * Once computed, the statistics will generally be "set" back on the
   10290             :  * owing dataset.
   10291             :  *
   10292             :  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
   10293             :  *
   10294             :  * This method is the same as the C functions GDALMDArrayComputeStatistics().
   10295             :  * and GDALMDArrayComputeStatisticsEx().
   10296             :  *
   10297             :  * @param bApproxOK Currently ignored. In the future, should be set to true
   10298             :  * if statistics on the whole array are wished, or to false if a subset of it
   10299             :  * may be used.
   10300             :  *
   10301             :  * @param pdfMin Location into which to load image minimum (may be NULL).
   10302             :  *
   10303             :  * @param pdfMax Location into which to load image maximum (may be NULL).-
   10304             :  *
   10305             :  * @param pdfMean Location into which to load image mean (may be NULL).
   10306             :  *
   10307             :  * @param pdfStdDev Location into which to load image standard deviation
   10308             :  * (may be NULL).
   10309             :  *
   10310             :  * @param pnValidCount Number of samples whose value is different from the
   10311             :  * nodata value. (may be NULL)
   10312             :  *
   10313             :  * @param pfnProgress a function to call to report progress, or NULL.
   10314             :  *
   10315             :  * @param pProgressData application data to pass to the progress function.
   10316             :  *
   10317             :  * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
   10318             :  *                     Options are driver specific. For now the netCDF and Zarr
   10319             :  *                     drivers recognize UPDATE_METADATA=YES, whose effect is
   10320             :  *                     to add or update the actual_range attribute with the
   10321             :  *                     computed min/max, only if done on the full array, in non
   10322             :  *                     approximate mode, and the dataset is opened in update
   10323             :  *                     mode.
   10324             :  *
   10325             :  * @return true on success
   10326             :  *
   10327             :  * @since GDAL 3.2
   10328             :  */
   10329             : 
   10330          13 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
   10331             :                                     double *pdfMax, double *pdfMean,
   10332             :                                     double *pdfStdDev, GUInt64 *pnValidCount,
   10333             :                                     GDALProgressFunc pfnProgress,
   10334             :                                     void *pProgressData,
   10335             :                                     CSLConstList papszOptions)
   10336             : {
   10337             :     struct StatsPerChunkType
   10338             :     {
   10339             :         const GDALMDArray *array = nullptr;
   10340             :         std::shared_ptr<GDALMDArray> poMask{};
   10341             :         double dfMin = cpl::NumericLimits<double>::max();
   10342             :         double dfMax = -cpl::NumericLimits<double>::max();
   10343             :         double dfMean = 0.0;
   10344             :         double dfM2 = 0.0;
   10345             :         GUInt64 nValidCount = 0;
   10346             :         std::vector<GByte> abyData{};
   10347             :         std::vector<double> adfData{};
   10348             :         std::vector<GByte> abyMaskData{};
   10349             :         GDALProgressFunc pfnProgress = nullptr;
   10350             :         void *pProgressData = nullptr;
   10351             :     };
   10352             : 
   10353          13 :     const auto PerChunkFunc = [](GDALAbstractMDArray *,
   10354             :                                  const GUInt64 *chunkArrayStartIdx,
   10355             :                                  const size_t *chunkCount, GUInt64 iCurChunk,
   10356             :                                  GUInt64 nChunkCount, void *pUserData)
   10357             :     {
   10358          13 :         StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
   10359          13 :         const GDALMDArray *array = data->array;
   10360          13 :         const GDALMDArray *poMask = data->poMask.get();
   10361          13 :         const size_t nDims = array->GetDimensionCount();
   10362          13 :         size_t nVals = 1;
   10363          34 :         for (size_t i = 0; i < nDims; i++)
   10364          21 :             nVals *= chunkCount[i];
   10365             : 
   10366             :         // Get mask
   10367          13 :         data->abyMaskData.resize(nVals);
   10368          13 :         if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
   10369          13 :                            poMask->GetDataType(), &data->abyMaskData[0])))
   10370             :         {
   10371           0 :             return false;
   10372             :         }
   10373             : 
   10374             :         // Get data
   10375          13 :         const auto &oType = array->GetDataType();
   10376          13 :         if (oType.GetNumericDataType() == GDT_Float64)
   10377             :         {
   10378           6 :             data->adfData.resize(nVals);
   10379           6 :             if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
   10380           6 :                              oType, &data->adfData[0]))
   10381             :             {
   10382           0 :                 return false;
   10383             :             }
   10384             :         }
   10385             :         else
   10386             :         {
   10387           7 :             data->abyData.resize(nVals * oType.GetSize());
   10388           7 :             if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
   10389           7 :                              oType, &data->abyData[0]))
   10390             :             {
   10391           0 :                 return false;
   10392             :             }
   10393           7 :             data->adfData.resize(nVals);
   10394           7 :             GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
   10395           7 :                             static_cast<int>(oType.GetSize()),
   10396           7 :                             &data->adfData[0], GDT_Float64,
   10397             :                             static_cast<int>(sizeof(double)),
   10398             :                             static_cast<GPtrDiff_t>(nVals));
   10399             :         }
   10400         912 :         for (size_t i = 0; i < nVals; i++)
   10401             :         {
   10402         899 :             if (data->abyMaskData[i])
   10403             :             {
   10404         894 :                 const double dfValue = data->adfData[i];
   10405         894 :                 data->dfMin = std::min(data->dfMin, dfValue);
   10406         894 :                 data->dfMax = std::max(data->dfMax, dfValue);
   10407         894 :                 data->nValidCount++;
   10408         894 :                 const double dfDelta = dfValue - data->dfMean;
   10409         894 :                 data->dfMean += dfDelta / data->nValidCount;
   10410         894 :                 data->dfM2 += dfDelta * (dfValue - data->dfMean);
   10411             :             }
   10412             :         }
   10413          13 :         if (data->pfnProgress &&
   10414           0 :             !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
   10415             :                                "", data->pProgressData))
   10416             :         {
   10417           0 :             return false;
   10418             :         }
   10419          13 :         return true;
   10420             :     };
   10421             : 
   10422          13 :     const auto &oType = GetDataType();
   10423          26 :     if (oType.GetClass() != GEDTC_NUMERIC ||
   10424          13 :         GDALDataTypeIsComplex(oType.GetNumericDataType()))
   10425             :     {
   10426           0 :         CPLError(
   10427             :             CE_Failure, CPLE_NotSupported,
   10428             :             "Statistics can only be computed on non-complex numeric data type");
   10429           0 :         return false;
   10430             :     }
   10431             : 
   10432          13 :     const size_t nDims = GetDimensionCount();
   10433          26 :     std::vector<GUInt64> arrayStartIdx(nDims);
   10434          26 :     std::vector<GUInt64> count(nDims);
   10435          13 :     const auto &poDims = GetDimensions();
   10436          34 :     for (size_t i = 0; i < nDims; i++)
   10437             :     {
   10438          21 :         count[i] = poDims[i]->GetSize();
   10439             :     }
   10440          13 :     const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
   10441             :     const size_t nMaxChunkSize =
   10442             :         pszSwathSize
   10443          13 :             ? static_cast<size_t>(
   10444           0 :                   std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
   10445           0 :                            CPLAtoGIntBig(pszSwathSize)))
   10446             :             : static_cast<size_t>(
   10447          13 :                   std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
   10448          13 :                            GDALGetCacheMax64() / 4));
   10449          26 :     StatsPerChunkType sData;
   10450          13 :     sData.array = this;
   10451          13 :     sData.poMask = GetMask(nullptr);
   10452          13 :     if (sData.poMask == nullptr)
   10453             :     {
   10454           0 :         return false;
   10455             :     }
   10456          13 :     sData.pfnProgress = pfnProgress;
   10457          13 :     sData.pProgressData = pProgressData;
   10458          13 :     if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
   10459          26 :                          GetProcessingChunkSize(nMaxChunkSize).data(),
   10460          13 :                          PerChunkFunc, &sData))
   10461             :     {
   10462           0 :         return false;
   10463             :     }
   10464             : 
   10465          13 :     if (pdfMin)
   10466          13 :         *pdfMin = sData.dfMin;
   10467             : 
   10468          13 :     if (pdfMax)
   10469          13 :         *pdfMax = sData.dfMax;
   10470             : 
   10471          13 :     if (pdfMean)
   10472          11 :         *pdfMean = sData.dfMean;
   10473             : 
   10474             :     const double dfStdDev =
   10475          13 :         sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
   10476          13 :     if (pdfStdDev)
   10477          11 :         *pdfStdDev = dfStdDev;
   10478             : 
   10479          13 :     if (pnValidCount)
   10480          11 :         *pnValidCount = sData.nValidCount;
   10481             : 
   10482          13 :     SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
   10483          13 :                   sData.nValidCount, papszOptions);
   10484             : 
   10485          13 :     return true;
   10486             : }
   10487             : 
   10488             : /************************************************************************/
   10489             : /*                            SetStatistics()                           */
   10490             : /************************************************************************/
   10491             : //! @cond Doxygen_Suppress
   10492           5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
   10493             :                                 double /* dfMax */, double /* dfMean */,
   10494             :                                 double /* dfStdDev */,
   10495             :                                 GUInt64 /* nValidCount */,
   10496             :                                 CSLConstList /* papszOptions */)
   10497             : {
   10498           5 :     CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
   10499           5 :     return false;
   10500             : }
   10501             : 
   10502             : //! @endcond
   10503             : 
   10504             : /************************************************************************/
   10505             : /*                           ClearStatistics()                          */
   10506             : /************************************************************************/
   10507             : 
   10508             : /**
   10509             :  * \brief Clear statistics.
   10510             :  *
   10511             :  * @since GDAL 3.4
   10512             :  */
   10513           0 : void GDALMDArray::ClearStatistics()
   10514             : {
   10515           0 : }
   10516             : 
   10517             : /************************************************************************/
   10518             : /*                      GetCoordinateVariables()                        */
   10519             : /************************************************************************/
   10520             : 
   10521             : /**
   10522             :  * \brief Return coordinate variables.
   10523             :  *
   10524             :  * Coordinate variables are an alternate way of indexing an array that can
   10525             :  * be sometimes used. For example, an array collected through remote sensing
   10526             :  * might be indexed by (scanline, pixel). But there can be
   10527             :  * a longitude and latitude arrays alongside that are also both indexed by
   10528             :  * (scanline, pixel), and are referenced from operational arrays for
   10529             :  * reprojection purposes.
   10530             :  *
   10531             :  * For netCDF, this will return the arrays referenced by the "coordinates"
   10532             :  * attribute.
   10533             :  *
   10534             :  * This method is the same as the C function
   10535             :  * GDALMDArrayGetCoordinateVariables().
   10536             :  *
   10537             :  * @return a vector of arrays
   10538             :  *
   10539             :  * @since GDAL 3.4
   10540             :  */
   10541             : 
   10542             : std::vector<std::shared_ptr<GDALMDArray>>
   10543          13 : GDALMDArray::GetCoordinateVariables() const
   10544             : {
   10545          13 :     return {};
   10546             : }
   10547             : 
   10548             : /************************************************************************/
   10549             : /*                       ~GDALExtendedDataType()                        */
   10550             : /************************************************************************/
   10551             : 
   10552             : GDALExtendedDataType::~GDALExtendedDataType() = default;
   10553             : 
   10554             : /************************************************************************/
   10555             : /*                        GDALExtendedDataType()                        */
   10556             : /************************************************************************/
   10557             : 
   10558       14193 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
   10559       14193 :                                            GDALExtendedDataTypeSubType eSubType)
   10560             :     : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
   10561       14193 :       m_nMaxStringLength(nMaxStringLength)
   10562             : {
   10563       14193 : }
   10564             : 
   10565             : /************************************************************************/
   10566             : /*                        GDALExtendedDataType()                        */
   10567             : /************************************************************************/
   10568             : 
   10569       63887 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
   10570             :     : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
   10571       63887 :       m_nSize(GDALGetDataTypeSizeBytes(eType))
   10572             : {
   10573       63887 : }
   10574             : 
   10575             : /************************************************************************/
   10576             : /*                        GDALExtendedDataType()                        */
   10577             : /************************************************************************/
   10578             : 
   10579          63 : GDALExtendedDataType::GDALExtendedDataType(
   10580             :     const std::string &osName, GDALDataType eBaseType,
   10581          63 :     std::unique_ptr<GDALRasterAttributeTable> poRAT)
   10582             :     : m_osName(osName), m_eClass(GEDTC_NUMERIC), m_eNumericDT(eBaseType),
   10583          63 :       m_nSize(GDALGetDataTypeSizeBytes(eBaseType)), m_poRAT(std::move(poRAT))
   10584             : {
   10585          63 : }
   10586             : 
   10587             : /************************************************************************/
   10588             : /*                        GDALExtendedDataType()                        */
   10589             : /************************************************************************/
   10590             : 
   10591         915 : GDALExtendedDataType::GDALExtendedDataType(
   10592             :     const std::string &osName, size_t nTotalSize,
   10593         915 :     std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
   10594             :     : m_osName(osName), m_eClass(GEDTC_COMPOUND),
   10595         915 :       m_aoComponents(std::move(components)), m_nSize(nTotalSize)
   10596             : {
   10597         915 : }
   10598             : 
   10599             : /************************************************************************/
   10600             : /*                        GDALExtendedDataType()                        */
   10601             : /************************************************************************/
   10602             : 
   10603             : /** Move constructor. */
   10604             : GDALExtendedDataType::GDALExtendedDataType(GDALExtendedDataType &&) = default;
   10605             : 
   10606             : /************************************************************************/
   10607             : /*                        GDALExtendedDataType()                        */
   10608             : /************************************************************************/
   10609             : 
   10610             : /** Copy constructor. */
   10611       16948 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
   10612       33896 :     : m_osName(other.m_osName), m_eClass(other.m_eClass),
   10613       16948 :       m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
   10614       16948 :       m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength),
   10615       16948 :       m_poRAT(other.m_poRAT ? other.m_poRAT->Clone() : nullptr)
   10616             : {
   10617       16948 :     if (m_eClass == GEDTC_COMPOUND)
   10618             :     {
   10619         481 :         for (const auto &elt : other.m_aoComponents)
   10620             :         {
   10621         318 :             m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
   10622             :         }
   10623             :     }
   10624       16948 : }
   10625             : 
   10626             : /************************************************************************/
   10627             : /*                            operator= ()                              */
   10628             : /************************************************************************/
   10629             : 
   10630             : /** Copy assignment. */
   10631             : GDALExtendedDataType &
   10632        1072 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
   10633             : {
   10634        1072 :     if (this != &other)
   10635             :     {
   10636        1072 :         m_osName = other.m_osName;
   10637        1072 :         m_eClass = other.m_eClass;
   10638        1072 :         m_eSubType = other.m_eSubType;
   10639        1072 :         m_eNumericDT = other.m_eNumericDT;
   10640        1072 :         m_nSize = other.m_nSize;
   10641        1072 :         m_nMaxStringLength = other.m_nMaxStringLength;
   10642        1072 :         m_poRAT.reset(other.m_poRAT ? other.m_poRAT->Clone() : nullptr);
   10643        1072 :         m_aoComponents.clear();
   10644        1072 :         if (m_eClass == GEDTC_COMPOUND)
   10645             :         {
   10646           0 :             for (const auto &elt : other.m_aoComponents)
   10647             :             {
   10648           0 :                 m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
   10649             :             }
   10650             :         }
   10651             :     }
   10652        1072 :     return *this;
   10653             : }
   10654             : 
   10655             : /************************************************************************/
   10656             : /*                            operator= ()                              */
   10657             : /************************************************************************/
   10658             : 
   10659             : /** Move assignment. */
   10660             : GDALExtendedDataType &
   10661             : GDALExtendedDataType::operator=(GDALExtendedDataType &&other) = default;
   10662             : 
   10663             : /************************************************************************/
   10664             : /*                           Create()                                   */
   10665             : /************************************************************************/
   10666             : 
   10667             : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
   10668             :  *
   10669             :  * This is the same as the C function GDALExtendedDataTypeCreate()
   10670             :  *
   10671             :  * @param eType Numeric data type. Must be different from GDT_Unknown and
   10672             :  * GDT_TypeCount
   10673             :  */
   10674       63880 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
   10675             : {
   10676       63880 :     return GDALExtendedDataType(eType);
   10677             : }
   10678             : 
   10679             : /************************************************************************/
   10680             : /*                           Create()                                   */
   10681             : /************************************************************************/
   10682             : 
   10683             : /** Return a new GDALExtendedDataType from a raster attribute table.
   10684             :  *
   10685             :  * @param osName Type name
   10686             :  * @param eBaseType Base integer data type.
   10687             :  * @param poRAT Raster attribute table. Must not be NULL.
   10688             :  * @since 3.12
   10689             :  */
   10690             : GDALExtendedDataType
   10691          63 : GDALExtendedDataType::Create(const std::string &osName, GDALDataType eBaseType,
   10692             :                              std::unique_ptr<GDALRasterAttributeTable> poRAT)
   10693             : {
   10694          63 :     return GDALExtendedDataType(osName, eBaseType, std::move(poRAT));
   10695             : }
   10696             : 
   10697             : /************************************************************************/
   10698             : /*                           Create()                                   */
   10699             : /************************************************************************/
   10700             : 
   10701             : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
   10702             :  *
   10703             :  * This is the same as the C function GDALExtendedDataTypeCreateCompound()
   10704             :  *
   10705             :  * @param osName Type name.
   10706             :  * @param nTotalSize Total size of the type in bytes.
   10707             :  *                   Should be large enough to store all components.
   10708             :  * @param components Components of the compound type.
   10709             :  */
   10710         922 : GDALExtendedDataType GDALExtendedDataType::Create(
   10711             :     const std::string &osName, size_t nTotalSize,
   10712             :     std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
   10713             : {
   10714         922 :     size_t nLastOffset = 0;
   10715             :     // Some arbitrary threshold to avoid potential integer overflows
   10716         922 :     if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
   10717             :     {
   10718           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10719           2 :         return GDALExtendedDataType(GDT_Unknown);
   10720             :     }
   10721        4306 :     for (const auto &comp : components)
   10722             :     {
   10723             :         // Check alignment too ?
   10724        3387 :         if (comp->GetOffset() < nLastOffset)
   10725             :         {
   10726           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10727           1 :             return GDALExtendedDataType(GDT_Unknown);
   10728             :         }
   10729        3386 :         nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
   10730             :     }
   10731         919 :     if (nTotalSize < nLastOffset)
   10732             :     {
   10733           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10734           1 :         return GDALExtendedDataType(GDT_Unknown);
   10735             :     }
   10736         918 :     if (nTotalSize == 0 || components.empty())
   10737             :     {
   10738           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
   10739           3 :         return GDALExtendedDataType(GDT_Unknown);
   10740             :     }
   10741         915 :     return GDALExtendedDataType(osName, nTotalSize, std::move(components));
   10742             : }
   10743             : 
   10744             : /************************************************************************/
   10745             : /*                           Create()                                   */
   10746             : /************************************************************************/
   10747             : 
   10748             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   10749             :  *
   10750             :  * This is the same as the C function GDALExtendedDataTypeCreateString().
   10751             :  *
   10752             :  * @param nMaxStringLength maximum length of a string in bytes. 0 if
   10753             :  * unknown/unlimited
   10754             :  * @param eSubType Subtype.
   10755             :  */
   10756             : GDALExtendedDataType
   10757       14193 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
   10758             :                                    GDALExtendedDataTypeSubType eSubType)
   10759             : {
   10760       14193 :     return GDALExtendedDataType(nMaxStringLength, eSubType);
   10761             : }
   10762             : 
   10763             : /************************************************************************/
   10764             : /*                           operator==()                               */
   10765             : /************************************************************************/
   10766             : 
   10767             : /** Equality operator.
   10768             :  *
   10769             :  * This is the same as the C function GDALExtendedDataTypeEquals().
   10770             :  */
   10771        3138 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
   10772             : {
   10773        3111 :     if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
   10774        6249 :         m_nSize != other.m_nSize || m_osName != other.m_osName)
   10775             :     {
   10776         324 :         return false;
   10777             :     }
   10778        2814 :     if (m_eClass == GEDTC_NUMERIC)
   10779             :     {
   10780         935 :         return m_eNumericDT == other.m_eNumericDT;
   10781             :     }
   10782        1879 :     if (m_eClass == GEDTC_STRING)
   10783             :     {
   10784        1632 :         return true;
   10785             :     }
   10786         247 :     CPLAssert(m_eClass == GEDTC_COMPOUND);
   10787         247 :     if (m_aoComponents.size() != other.m_aoComponents.size())
   10788             :     {
   10789           2 :         return false;
   10790             :     }
   10791        1232 :     for (size_t i = 0; i < m_aoComponents.size(); i++)
   10792             :     {
   10793         987 :         if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
   10794             :         {
   10795           0 :             return false;
   10796             :         }
   10797             :     }
   10798         245 :     return true;
   10799             : }
   10800             : 
   10801             : /************************************************************************/
   10802             : /*                        CanConvertTo()                                */
   10803             : /************************************************************************/
   10804             : 
   10805             : /** Return whether this data type can be converted to the other one.
   10806             :  *
   10807             :  * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
   10808             :  *
   10809             :  * @param other Target data type for the conversion being considered.
   10810             :  */
   10811       10531 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
   10812             : {
   10813       10531 :     if (m_eClass == GEDTC_NUMERIC)
   10814             :     {
   10815        7189 :         if (m_eNumericDT == GDT_Unknown)
   10816           0 :             return false;
   10817        7189 :         if (other.m_eClass == GEDTC_NUMERIC &&
   10818        6980 :             other.m_eNumericDT == GDT_Unknown)
   10819           0 :             return false;
   10820        7398 :         return other.m_eClass == GEDTC_NUMERIC ||
   10821        7398 :                other.m_eClass == GEDTC_STRING;
   10822             :     }
   10823        3342 :     if (m_eClass == GEDTC_STRING)
   10824             :     {
   10825        3121 :         return other.m_eClass == m_eClass;
   10826             :     }
   10827         221 :     CPLAssert(m_eClass == GEDTC_COMPOUND);
   10828         221 :     if (other.m_eClass != GEDTC_COMPOUND)
   10829           0 :         return false;
   10830             :     std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
   10831         442 :         srcComponents;
   10832        1052 :     for (const auto &srcComp : m_aoComponents)
   10833             :     {
   10834         831 :         srcComponents[srcComp->GetName()] = &srcComp;
   10835             :     }
   10836         583 :     for (const auto &dstComp : other.m_aoComponents)
   10837             :     {
   10838         363 :         auto oIter = srcComponents.find(dstComp->GetName());
   10839         363 :         if (oIter == srcComponents.end())
   10840           1 :             return false;
   10841         362 :         if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
   10842           0 :             return false;
   10843             :     }
   10844         220 :     return true;
   10845             : }
   10846             : 
   10847             : /************************************************************************/
   10848             : /*                     NeedsFreeDynamicMemory()                         */
   10849             : /************************************************************************/
   10850             : 
   10851             : /** Return whether the data type holds dynamically allocated memory, that
   10852             :  * needs to be freed with FreeDynamicMemory().
   10853             :  *
   10854             :  */
   10855        4011 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
   10856             : {
   10857        4011 :     switch (m_eClass)
   10858             :     {
   10859        1060 :         case GEDTC_STRING:
   10860        1060 :             return true;
   10861             : 
   10862        2828 :         case GEDTC_NUMERIC:
   10863        2828 :             return false;
   10864             : 
   10865         123 :         case GEDTC_COMPOUND:
   10866             :         {
   10867         244 :             for (const auto &comp : m_aoComponents)
   10868             :             {
   10869         222 :                 if (comp->GetType().NeedsFreeDynamicMemory())
   10870         101 :                     return true;
   10871             :             }
   10872             :         }
   10873             :     }
   10874          22 :     return false;
   10875             : }
   10876             : 
   10877             : /************************************************************************/
   10878             : /*                        FreeDynamicMemory()                           */
   10879             : /************************************************************************/
   10880             : 
   10881             : /** Release the dynamic memory (strings typically) from a raw value.
   10882             :  *
   10883             :  * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
   10884             :  *
   10885             :  * @param pBuffer Raw buffer of a single element of an attribute or array value.
   10886             :  */
   10887        4163 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
   10888             : {
   10889        4163 :     switch (m_eClass)
   10890             :     {
   10891        2998 :         case GEDTC_STRING:
   10892             :         {
   10893             :             char *pszStr;
   10894        2998 :             memcpy(&pszStr, pBuffer, sizeof(char *));
   10895        2998 :             if (pszStr)
   10896             :             {
   10897        2400 :                 VSIFree(pszStr);
   10898             :             }
   10899        2998 :             break;
   10900             :         }
   10901             : 
   10902         976 :         case GEDTC_NUMERIC:
   10903             :         {
   10904         976 :             break;
   10905             :         }
   10906             : 
   10907         189 :         case GEDTC_COMPOUND:
   10908             :         {
   10909         189 :             GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
   10910         999 :             for (const auto &comp : m_aoComponents)
   10911             :             {
   10912        1620 :                 comp->GetType().FreeDynamicMemory(pabyBuffer +
   10913         810 :                                                   comp->GetOffset());
   10914             :             }
   10915         189 :             break;
   10916             :         }
   10917             :     }
   10918        4163 : }
   10919             : 
   10920             : /************************************************************************/
   10921             : /*                      ~GDALEDTComponent()                             */
   10922             : /************************************************************************/
   10923             : 
   10924             : GDALEDTComponent::~GDALEDTComponent() = default;
   10925             : 
   10926             : /************************************************************************/
   10927             : /*                      GDALEDTComponent()                              */
   10928             : /************************************************************************/
   10929             : 
   10930             : /** constructor of a GDALEDTComponent
   10931             :  *
   10932             :  * This is the same as the C function GDALEDTComponendCreate()
   10933             :  *
   10934             :  * @param name Component name
   10935             :  * @param offset Offset in byte of the component in the compound data type.
   10936             :  *               In case of nesting of compound data type, this should be
   10937             :  *               the offset to the immediate belonging data type, not to the
   10938             :  *               higher level one.
   10939             :  * @param type   Component data type.
   10940             :  */
   10941        3378 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
   10942        3378 :                                    const GDALExtendedDataType &type)
   10943        3378 :     : m_osName(name), m_nOffset(offset), m_oType(type)
   10944             : {
   10945        3378 : }
   10946             : 
   10947             : /************************************************************************/
   10948             : /*                      GDALEDTComponent()                              */
   10949             : /************************************************************************/
   10950             : 
   10951             : /** Copy constructor. */
   10952             : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
   10953             : 
   10954             : /************************************************************************/
   10955             : /*                           operator==()                               */
   10956             : /************************************************************************/
   10957             : 
   10958             : /** Equality operator.
   10959             :  */
   10960         987 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
   10961             : {
   10962        1974 :     return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
   10963        1974 :            m_oType == other.m_oType;
   10964             : }
   10965             : 
   10966             : /************************************************************************/
   10967             : /*                        ~GDALDimension()                              */
   10968             : /************************************************************************/
   10969             : 
   10970             : GDALDimension::~GDALDimension() = default;
   10971             : 
   10972             : /************************************************************************/
   10973             : /*                         GDALDimension()                              */
   10974             : /************************************************************************/
   10975             : 
   10976             : //! @cond Doxygen_Suppress
   10977             : /** Constructor.
   10978             :  *
   10979             :  * @param osParentName Parent name
   10980             :  * @param osName name
   10981             :  * @param osType type. See GetType().
   10982             :  * @param osDirection direction. See GetDirection().
   10983             :  * @param nSize size.
   10984             :  */
   10985        9315 : GDALDimension::GDALDimension(const std::string &osParentName,
   10986             :                              const std::string &osName,
   10987             :                              const std::string &osType,
   10988        9315 :                              const std::string &osDirection, GUInt64 nSize)
   10989             :     : m_osName(osName),
   10990             :       m_osFullName(
   10991        9315 :           !osParentName.empty()
   10992       13770 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
   10993             :               : osName),
   10994       32400 :       m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
   10995             : {
   10996        9315 : }
   10997             : 
   10998             : //! @endcond
   10999             : 
   11000             : /************************************************************************/
   11001             : /*                         GetIndexingVariable()                        */
   11002             : /************************************************************************/
   11003             : 
   11004             : /** Return the variable that is used to index the dimension (if there is one).
   11005             :  *
   11006             :  * This is the array, typically one-dimensional, describing the values taken
   11007             :  * by the dimension.
   11008             :  */
   11009          43 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
   11010             : {
   11011          43 :     return nullptr;
   11012             : }
   11013             : 
   11014             : /************************************************************************/
   11015             : /*                         SetIndexingVariable()                        */
   11016             : /************************************************************************/
   11017             : 
   11018             : /** Set the variable that is used to index the dimension.
   11019             :  *
   11020             :  * This is the array, typically one-dimensional, describing the values taken
   11021             :  * by the dimension.
   11022             :  *
   11023             :  * Optionally implemented by drivers.
   11024             :  *
   11025             :  * Drivers known to implement it: MEM.
   11026             :  *
   11027             :  * @param poArray Variable to use to index the dimension.
   11028             :  * @return true in case of success.
   11029             :  */
   11030          11 : bool GDALDimension::SetIndexingVariable(
   11031             :     CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
   11032             : {
   11033          11 :     CPLError(CE_Failure, CPLE_NotSupported,
   11034             :              "SetIndexingVariable() not implemented");
   11035          11 :     return false;
   11036             : }
   11037             : 
   11038             : /************************************************************************/
   11039             : /*                            Rename()                                  */
   11040             : /************************************************************************/
   11041             : 
   11042             : /** Rename the dimension.
   11043             :  *
   11044             :  * This is not implemented by all drivers.
   11045             :  *
   11046             :  * Drivers known to implement it: MEM, netCDF, ZARR.
   11047             :  *
   11048             :  * This is the same as the C function GDALDimensionRename().
   11049             :  *
   11050             :  * @param osNewName New name.
   11051             :  *
   11052             :  * @return true in case of success
   11053             :  * @since GDAL 3.8
   11054             :  */
   11055           0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
   11056             : {
   11057           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
   11058           0 :     return false;
   11059             : }
   11060             : 
   11061             : /************************************************************************/
   11062             : /*                         BaseRename()                                 */
   11063             : /************************************************************************/
   11064             : 
   11065             : //! @cond Doxygen_Suppress
   11066           8 : void GDALDimension::BaseRename(const std::string &osNewName)
   11067             : {
   11068           8 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
   11069           8 :     m_osFullName += osNewName;
   11070           8 :     m_osName = osNewName;
   11071           8 : }
   11072             : 
   11073             : //! @endcond
   11074             : 
   11075             : //! @cond Doxygen_Suppress
   11076             : /************************************************************************/
   11077             : /*                          ParentRenamed()                             */
   11078             : /************************************************************************/
   11079             : 
   11080           8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
   11081             : {
   11082           8 :     m_osFullName = osNewParentFullName;
   11083           8 :     m_osFullName += "/";
   11084           8 :     m_osFullName += m_osName;
   11085           8 : }
   11086             : 
   11087             : //! @endcond
   11088             : 
   11089             : //! @cond Doxygen_Suppress
   11090             : /************************************************************************/
   11091             : /*                          ParentDeleted()                             */
   11092             : /************************************************************************/
   11093             : 
   11094           4 : void GDALDimension::ParentDeleted()
   11095             : {
   11096           4 : }
   11097             : 
   11098             : //! @endcond
   11099             : 
   11100             : /************************************************************************/
   11101             : /************************************************************************/
   11102             : /************************************************************************/
   11103             : /*                              C API                                   */
   11104             : /************************************************************************/
   11105             : /************************************************************************/
   11106             : /************************************************************************/
   11107             : 
   11108             : /************************************************************************/
   11109             : /*                      GDALExtendedDataTypeCreate()                    */
   11110             : /************************************************************************/
   11111             : 
   11112             : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
   11113             :  *
   11114             :  * This is the same as the C++ method GDALExtendedDataType::Create()
   11115             :  *
   11116             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   11117             :  *
   11118             :  * @param eType Numeric data type. Must be different from GDT_Unknown and
   11119             :  * GDT_TypeCount
   11120             :  *
   11121             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   11122             :  */
   11123        2146 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
   11124             : {
   11125        2146 :     if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
   11126             :     {
   11127           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
   11128             :                  "Illegal GDT_Unknown/GDT_TypeCount argument");
   11129           0 :         return nullptr;
   11130             :     }
   11131             :     return new GDALExtendedDataTypeHS(
   11132        2146 :         new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
   11133             : }
   11134             : 
   11135             : /************************************************************************/
   11136             : /*                    GDALExtendedDataTypeCreateString()                */
   11137             : /************************************************************************/
   11138             : 
   11139             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   11140             :  *
   11141             :  * This is the same as the C++ method GDALExtendedDataType::CreateString()
   11142             :  *
   11143             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   11144             :  *
   11145             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   11146             :  */
   11147           0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
   11148             : {
   11149           0 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   11150           0 :         GDALExtendedDataType::CreateString(nMaxStringLength)));
   11151             : }
   11152             : 
   11153             : /************************************************************************/
   11154             : /*                   GDALExtendedDataTypeCreateStringEx()               */
   11155             : /************************************************************************/
   11156             : 
   11157             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   11158             :  *
   11159             :  * This is the same as the C++ method GDALExtendedDataType::CreateString()
   11160             :  *
   11161             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   11162             :  *
   11163             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   11164             :  * @since GDAL 3.4
   11165             :  */
   11166             : GDALExtendedDataTypeH
   11167         222 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
   11168             :                                    GDALExtendedDataTypeSubType eSubType)
   11169             : {
   11170         222 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   11171         222 :         GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
   11172             : }
   11173             : 
   11174             : /************************************************************************/
   11175             : /*                   GDALExtendedDataTypeCreateCompound()               */
   11176             : /************************************************************************/
   11177             : 
   11178             : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
   11179             :  *
   11180             :  * This is the same as the C++ method GDALExtendedDataType::Create(const
   11181             :  * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
   11182             :  *
   11183             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   11184             :  *
   11185             :  * @param pszName Type name.
   11186             :  * @param nTotalSize Total size of the type in bytes.
   11187             :  *                   Should be large enough to store all components.
   11188             :  * @param nComponents Number of components in comps array.
   11189             :  * @param comps Components.
   11190             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   11191             :  */
   11192             : GDALExtendedDataTypeH
   11193          22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
   11194             :                                    size_t nComponents,
   11195             :                                    const GDALEDTComponentH *comps)
   11196             : {
   11197          44 :     std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
   11198          54 :     for (size_t i = 0; i < nComponents; i++)
   11199             :     {
   11200             :         compsCpp.emplace_back(
   11201          32 :             std::make_unique<GDALEDTComponent>(*(comps[i]->m_poImpl.get())));
   11202             :     }
   11203             :     auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
   11204          66 :                                            std::move(compsCpp));
   11205          22 :     if (dt.GetClass() != GEDTC_COMPOUND)
   11206           6 :         return nullptr;
   11207          16 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(std::move(dt)));
   11208             : }
   11209             : 
   11210             : /************************************************************************/
   11211             : /*                     GDALExtendedDataTypeRelease()                    */
   11212             : /************************************************************************/
   11213             : 
   11214             : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
   11215             :  *
   11216             :  * Note: when applied on a object coming from a driver, this does not
   11217             :  * destroy the object in the file, database, etc...
   11218             :  */
   11219        7069 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
   11220             : {
   11221        7069 :     delete hEDT;
   11222        7069 : }
   11223             : 
   11224             : /************************************************************************/
   11225             : /*                     GDALExtendedDataTypeGetName()                    */
   11226             : /************************************************************************/
   11227             : 
   11228             : /** Return type name.
   11229             :  *
   11230             :  * This is the same as the C++ method GDALExtendedDataType::GetName()
   11231             :  */
   11232           8 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
   11233             : {
   11234           8 :     VALIDATE_POINTER1(hEDT, __func__, "");
   11235           8 :     return hEDT->m_poImpl->GetName().c_str();
   11236             : }
   11237             : 
   11238             : /************************************************************************/
   11239             : /*                     GDALExtendedDataTypeGetClass()                    */
   11240             : /************************************************************************/
   11241             : 
   11242             : /** Return type class.
   11243             :  *
   11244             :  * This is the same as the C++ method GDALExtendedDataType::GetClass()
   11245             :  */
   11246             : GDALExtendedDataTypeClass
   11247        9591 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
   11248             : {
   11249        9591 :     VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
   11250        9591 :     return hEDT->m_poImpl->GetClass();
   11251             : }
   11252             : 
   11253             : /************************************************************************/
   11254             : /*               GDALExtendedDataTypeGetNumericDataType()               */
   11255             : /************************************************************************/
   11256             : 
   11257             : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
   11258             :  *
   11259             :  * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
   11260             :  */
   11261        2134 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
   11262             : {
   11263        2134 :     VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
   11264        2134 :     return hEDT->m_poImpl->GetNumericDataType();
   11265             : }
   11266             : 
   11267             : /************************************************************************/
   11268             : /*                   GDALExtendedDataTypeGetSize()                      */
   11269             : /************************************************************************/
   11270             : 
   11271             : /** Return data type size in bytes.
   11272             :  *
   11273             :  * This is the same as the C++ method GDALExtendedDataType::GetSize()
   11274             :  */
   11275        2663 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
   11276             : {
   11277        2663 :     VALIDATE_POINTER1(hEDT, __func__, 0);
   11278        2663 :     return hEDT->m_poImpl->GetSize();
   11279             : }
   11280             : 
   11281             : /************************************************************************/
   11282             : /*              GDALExtendedDataTypeGetMaxStringLength()                */
   11283             : /************************************************************************/
   11284             : 
   11285             : /** Return the maximum length of a string in bytes.
   11286             :  *
   11287             :  * 0 indicates unknown/unlimited string.
   11288             :  *
   11289             :  * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
   11290             :  */
   11291           3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
   11292             : {
   11293           3 :     VALIDATE_POINTER1(hEDT, __func__, 0);
   11294           3 :     return hEDT->m_poImpl->GetMaxStringLength();
   11295             : }
   11296             : 
   11297             : /************************************************************************/
   11298             : /*                    GDALExtendedDataTypeCanConvertTo()                */
   11299             : /************************************************************************/
   11300             : 
   11301             : /** Return whether this data type can be converted to the other one.
   11302             :  *
   11303             :  * This is the same as the C function GDALExtendedDataType::CanConvertTo()
   11304             :  *
   11305             :  * @param hSourceEDT Source data type for the conversion being considered.
   11306             :  * @param hTargetEDT Target data type for the conversion being considered.
   11307             :  * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
   11308             :  */
   11309           7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
   11310             :                                      GDALExtendedDataTypeH hTargetEDT)
   11311             : {
   11312           7 :     VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
   11313           7 :     VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
   11314           7 :     return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
   11315             : }
   11316             : 
   11317             : /************************************************************************/
   11318             : /*                        GDALExtendedDataTypeEquals()                  */
   11319             : /************************************************************************/
   11320             : 
   11321             : /** Return whether this data type is equal to another one.
   11322             :  *
   11323             :  * This is the same as the C++ method GDALExtendedDataType::operator==()
   11324             :  *
   11325             :  * @param hFirstEDT First data type.
   11326             :  * @param hSecondEDT Second data type.
   11327             :  * @return TRUE if they are equal. FALSE otherwise.
   11328             :  */
   11329         100 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
   11330             :                                GDALExtendedDataTypeH hSecondEDT)
   11331             : {
   11332         100 :     VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
   11333         100 :     VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
   11334         100 :     return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
   11335             : }
   11336             : 
   11337             : /************************************************************************/
   11338             : /*                    GDALExtendedDataTypeGetSubType()                  */
   11339             : /************************************************************************/
   11340             : 
   11341             : /** Return the subtype of a type.
   11342             :  *
   11343             :  * This is the same as the C++ method GDALExtendedDataType::GetSubType()
   11344             :  *
   11345             :  * @param hEDT Data type.
   11346             :  * @return subtype.
   11347             :  * @since 3.4
   11348             :  */
   11349             : GDALExtendedDataTypeSubType
   11350         105 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
   11351             : {
   11352         105 :     VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
   11353         105 :     return hEDT->m_poImpl->GetSubType();
   11354             : }
   11355             : 
   11356             : /************************************************************************/
   11357             : /*                      GDALExtendedDataTypeGetRAT()                    */
   11358             : /************************************************************************/
   11359             : 
   11360             : /** Return associated raster attribute table, when there is one.
   11361             :  *
   11362             :  * * For the netCDF driver, the RAT will capture enumerated types, with
   11363             :  * a "value" column with an integer value and a "name" column with the
   11364             :  * associated name.
   11365             :  * This is the same as the C++ method GDALExtendedDataType::GetRAT()
   11366             :  *
   11367             :  * @param hEDT Data type.
   11368             :  * @return raster attribute (owned by GDALExtendedDataTypeH), or NULL
   11369             :  * @since 3.12
   11370             :  */
   11371           1 : GDALRasterAttributeTableH GDALExtendedDataTypeGetRAT(GDALExtendedDataTypeH hEDT)
   11372             : {
   11373           1 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11374           1 :     return GDALRasterAttributeTable::ToHandle(
   11375           2 :         const_cast<GDALRasterAttributeTable *>(hEDT->m_poImpl->GetRAT()));
   11376             : }
   11377             : 
   11378             : /************************************************************************/
   11379             : /*                     GDALExtendedDataTypeGetComponents()              */
   11380             : /************************************************************************/
   11381             : 
   11382             : /** Return the components of the data type (only valid when GetClass() ==
   11383             :  * GEDTC_COMPOUND)
   11384             :  *
   11385             :  * The returned array and its content must be freed with
   11386             :  * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
   11387             :  * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
   11388             :  * individual array members).
   11389             :  *
   11390             :  * This is the same as the C++ method GDALExtendedDataType::GetComponents()
   11391             :  *
   11392             :  * @param hEDT Data type
   11393             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11394             :  * @return an array of *pnCount components.
   11395             :  */
   11396          44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
   11397             :                                                      size_t *pnCount)
   11398             : {
   11399          44 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11400          44 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11401          44 :     const auto &components = hEDT->m_poImpl->GetComponents();
   11402             :     auto ret = static_cast<GDALEDTComponentH *>(
   11403          44 :         CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
   11404         131 :     for (size_t i = 0; i < components.size(); i++)
   11405             :     {
   11406          87 :         ret[i] = new GDALEDTComponentHS(*components[i].get());
   11407             :     }
   11408          44 :     *pnCount = components.size();
   11409          44 :     return ret;
   11410             : }
   11411             : 
   11412             : /************************************************************************/
   11413             : /*                     GDALExtendedDataTypeFreeComponents()             */
   11414             : /************************************************************************/
   11415             : 
   11416             : /** Free the return of GDALExtendedDataTypeGetComponents().
   11417             :  *
   11418             :  * @param components return value of GDALExtendedDataTypeGetComponents()
   11419             :  * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
   11420             :  */
   11421          44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
   11422             :                                         size_t nCount)
   11423             : {
   11424         131 :     for (size_t i = 0; i < nCount; i++)
   11425             :     {
   11426          87 :         delete components[i];
   11427             :     }
   11428          44 :     CPLFree(components);
   11429          44 : }
   11430             : 
   11431             : /************************************************************************/
   11432             : /*                         GDALEDTComponentCreate()                     */
   11433             : /************************************************************************/
   11434             : 
   11435             : /** Create a new GDALEDTComponent.
   11436             :  *
   11437             :  * The returned value must be freed with GDALEDTComponentRelease().
   11438             :  *
   11439             :  * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
   11440             :  */
   11441          20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
   11442             :                                          GDALExtendedDataTypeH hType)
   11443             : {
   11444          20 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11445          20 :     VALIDATE_POINTER1(hType, __func__, nullptr);
   11446             :     return new GDALEDTComponentHS(
   11447          20 :         GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
   11448             : }
   11449             : 
   11450             : /************************************************************************/
   11451             : /*                         GDALEDTComponentRelease()                    */
   11452             : /************************************************************************/
   11453             : 
   11454             : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
   11455             :  *
   11456             :  * Note: when applied on a object coming from a driver, this does not
   11457             :  * destroy the object in the file, database, etc...
   11458             :  */
   11459          61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
   11460             : {
   11461          61 :     delete hComp;
   11462          61 : }
   11463             : 
   11464             : /************************************************************************/
   11465             : /*                         GDALEDTComponentGetName()                    */
   11466             : /************************************************************************/
   11467             : 
   11468             : /** Return the name.
   11469             :  *
   11470             :  * The returned pointer is valid until hComp is released.
   11471             :  *
   11472             :  * This is the same as the C++ method GDALEDTComponent::GetName().
   11473             :  */
   11474          33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
   11475             : {
   11476          33 :     VALIDATE_POINTER1(hComp, __func__, nullptr);
   11477          33 :     return hComp->m_poImpl->GetName().c_str();
   11478             : }
   11479             : 
   11480             : /************************************************************************/
   11481             : /*                       GDALEDTComponentGetOffset()                    */
   11482             : /************************************************************************/
   11483             : 
   11484             : /** Return the offset (in bytes) of the component in the compound data type.
   11485             :  *
   11486             :  * This is the same as the C++ method GDALEDTComponent::GetOffset().
   11487             :  */
   11488          31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
   11489             : {
   11490          31 :     VALIDATE_POINTER1(hComp, __func__, 0);
   11491          31 :     return hComp->m_poImpl->GetOffset();
   11492             : }
   11493             : 
   11494             : /************************************************************************/
   11495             : /*                       GDALEDTComponentGetType()                      */
   11496             : /************************************************************************/
   11497             : 
   11498             : /** Return the data type of the component.
   11499             :  *
   11500             :  * This is the same as the C++ method GDALEDTComponent::GetType().
   11501             :  */
   11502          93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
   11503             : {
   11504          93 :     VALIDATE_POINTER1(hComp, __func__, nullptr);
   11505             :     return new GDALExtendedDataTypeHS(
   11506          93 :         new GDALExtendedDataType(hComp->m_poImpl->GetType()));
   11507             : }
   11508             : 
   11509             : /************************************************************************/
   11510             : /*                           GDALGroupRelease()                         */
   11511             : /************************************************************************/
   11512             : 
   11513             : /** Release the GDAL in-memory object associated with a GDALGroupH.
   11514             :  *
   11515             :  * Note: when applied on a object coming from a driver, this does not
   11516             :  * destroy the object in the file, database, etc...
   11517             :  */
   11518        1511 : void GDALGroupRelease(GDALGroupH hGroup)
   11519             : {
   11520        1511 :     delete hGroup;
   11521        1511 : }
   11522             : 
   11523             : /************************************************************************/
   11524             : /*                           GDALGroupGetName()                         */
   11525             : /************************************************************************/
   11526             : 
   11527             : /** Return the name of the group.
   11528             :  *
   11529             :  * The returned pointer is valid until hGroup is released.
   11530             :  *
   11531             :  * This is the same as the C++ method GDALGroup::GetName().
   11532             :  */
   11533          95 : const char *GDALGroupGetName(GDALGroupH hGroup)
   11534             : {
   11535          95 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11536          95 :     return hGroup->m_poImpl->GetName().c_str();
   11537             : }
   11538             : 
   11539             : /************************************************************************/
   11540             : /*                         GDALGroupGetFullName()                       */
   11541             : /************************************************************************/
   11542             : 
   11543             : /** Return the full name of the group.
   11544             :  *
   11545             :  * The returned pointer is valid until hGroup is released.
   11546             :  *
   11547             :  * This is the same as the C++ method GDALGroup::GetFullName().
   11548             :  */
   11549          47 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
   11550             : {
   11551          47 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11552          47 :     return hGroup->m_poImpl->GetFullName().c_str();
   11553             : }
   11554             : 
   11555             : /************************************************************************/
   11556             : /*                          GDALGroupGetMDArrayNames()                  */
   11557             : /************************************************************************/
   11558             : 
   11559             : /** Return the list of multidimensional array names contained in this group.
   11560             :  *
   11561             :  * This is the same as the C++ method GDALGroup::GetGroupNames().
   11562             :  *
   11563             :  * @return the array names, to be freed with CSLDestroy()
   11564             :  */
   11565         331 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
   11566             : {
   11567         331 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11568         662 :     auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
   11569         662 :     CPLStringList res;
   11570         839 :     for (const auto &name : names)
   11571             :     {
   11572         508 :         res.AddString(name.c_str());
   11573             :     }
   11574         331 :     return res.StealList();
   11575             : }
   11576             : 
   11577             : /************************************************************************/
   11578             : /*                  GDALGroupGetMDArrayFullNamesRecursive()             */
   11579             : /************************************************************************/
   11580             : 
   11581             : /** Return the list of multidimensional array full names contained in this
   11582             :  * group and its subgroups.
   11583             :  *
   11584             :  * This is the same as the C++ method GDALGroup::GetMDArrayFullNamesRecursive().
   11585             :  *
   11586             :  * @return the array names, to be freed with CSLDestroy()
   11587             :  *
   11588             :  * @since 3.11
   11589             :  */
   11590           1 : char **GDALGroupGetMDArrayFullNamesRecursive(GDALGroupH hGroup,
   11591             :                                              CSLConstList papszGroupOptions,
   11592             :                                              CSLConstList papszArrayOptions)
   11593             : {
   11594           1 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11595           1 :     auto names = hGroup->m_poImpl->GetMDArrayFullNamesRecursive(
   11596           2 :         papszGroupOptions, papszArrayOptions);
   11597           2 :     CPLStringList res;
   11598           5 :     for (const auto &name : names)
   11599             :     {
   11600           4 :         res.AddString(name.c_str());
   11601             :     }
   11602           1 :     return res.StealList();
   11603             : }
   11604             : 
   11605             : /************************************************************************/
   11606             : /*                          GDALGroupOpenMDArray()                      */
   11607             : /************************************************************************/
   11608             : 
   11609             : /** Open and return a multidimensional array.
   11610             :  *
   11611             :  * This is the same as the C++ method GDALGroup::OpenMDArray().
   11612             :  *
   11613             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11614             :  */
   11615         820 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
   11616             :                                   CSLConstList papszOptions)
   11617             : {
   11618         820 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11619         820 :     VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
   11620        2460 :     auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
   11621        2460 :                                                papszOptions);
   11622         820 :     if (!array)
   11623          30 :         return nullptr;
   11624         790 :     return new GDALMDArrayHS(array);
   11625             : }
   11626             : 
   11627             : /************************************************************************/
   11628             : /*                  GDALGroupOpenMDArrayFromFullname()                  */
   11629             : /************************************************************************/
   11630             : 
   11631             : /** Open and return a multidimensional array from its fully qualified name.
   11632             :  *
   11633             :  * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
   11634             :  *
   11635             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11636             :  *
   11637             :  * @since GDAL 3.2
   11638             :  */
   11639          17 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
   11640             :                                               const char *pszFullname,
   11641             :                                               CSLConstList papszOptions)
   11642             : {
   11643          17 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11644          17 :     VALIDATE_POINTER1(pszFullname, __func__, nullptr);
   11645          17 :     auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
   11646          51 :         std::string(pszFullname), papszOptions);
   11647          17 :     if (!array)
   11648           2 :         return nullptr;
   11649          15 :     return new GDALMDArrayHS(array);
   11650             : }
   11651             : 
   11652             : /************************************************************************/
   11653             : /*                      GDALGroupResolveMDArray()                       */
   11654             : /************************************************************************/
   11655             : 
   11656             : /** Locate an array in a group and its subgroups by name.
   11657             :  *
   11658             :  * See GDALGroup::ResolveMDArray() for description of the behavior.
   11659             :  * @since GDAL 3.2
   11660             :  */
   11661          19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
   11662             :                                      const char *pszStartingPoint,
   11663             :                                      CSLConstList papszOptions)
   11664             : {
   11665          19 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11666          19 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11667          19 :     VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
   11668          19 :     auto array = hGroup->m_poImpl->ResolveMDArray(
   11669          57 :         std::string(pszName), std::string(pszStartingPoint), papszOptions);
   11670          19 :     if (!array)
   11671           2 :         return nullptr;
   11672          17 :     return new GDALMDArrayHS(array);
   11673             : }
   11674             : 
   11675             : /************************************************************************/
   11676             : /*                        GDALGroupGetGroupNames()                      */
   11677             : /************************************************************************/
   11678             : 
   11679             : /** Return the list of sub-groups contained in this group.
   11680             :  *
   11681             :  * This is the same as the C++ method GDALGroup::GetGroupNames().
   11682             :  *
   11683             :  * @return the group names, to be freed with CSLDestroy()
   11684             :  */
   11685          98 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
   11686             : {
   11687          98 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11688         196 :     auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
   11689         196 :     CPLStringList res;
   11690         221 :     for (const auto &name : names)
   11691             :     {
   11692         123 :         res.AddString(name.c_str());
   11693             :     }
   11694          98 :     return res.StealList();
   11695             : }
   11696             : 
   11697             : /************************************************************************/
   11698             : /*                           GDALGroupOpenGroup()                       */
   11699             : /************************************************************************/
   11700             : 
   11701             : /** Open and return a sub-group.
   11702             :  *
   11703             :  * This is the same as the C++ method GDALGroup::OpenGroup().
   11704             :  *
   11705             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11706             :  */
   11707         163 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11708             :                               CSLConstList papszOptions)
   11709             : {
   11710         163 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11711         163 :     VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
   11712             :     auto subGroup =
   11713         489 :         hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
   11714         163 :     if (!subGroup)
   11715          30 :         return nullptr;
   11716         133 :     return new GDALGroupHS(subGroup);
   11717             : }
   11718             : 
   11719             : /************************************************************************/
   11720             : /*                   GDALGroupGetVectorLayerNames()                     */
   11721             : /************************************************************************/
   11722             : 
   11723             : /** Return the list of layer names contained in this group.
   11724             :  *
   11725             :  * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
   11726             :  *
   11727             :  * @return the group names, to be freed with CSLDestroy()
   11728             :  * @since 3.4
   11729             :  */
   11730           8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
   11731             :                                     CSLConstList papszOptions)
   11732             : {
   11733           8 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11734          16 :     auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
   11735          16 :     CPLStringList res;
   11736          18 :     for (const auto &name : names)
   11737             :     {
   11738          10 :         res.AddString(name.c_str());
   11739             :     }
   11740           8 :     return res.StealList();
   11741             : }
   11742             : 
   11743             : /************************************************************************/
   11744             : /*                      GDALGroupOpenVectorLayer()                      */
   11745             : /************************************************************************/
   11746             : 
   11747             : /** Open and return a vector layer.
   11748             :  *
   11749             :  * This is the same as the C++ method GDALGroup::OpenVectorLayer().
   11750             :  *
   11751             :  * Note that the vector layer is owned by its parent GDALDatasetH, and thus
   11752             :  * the returned handled if only valid while the parent GDALDatasetH is kept
   11753             :  * opened.
   11754             :  *
   11755             :  * @return the vector layer, or nullptr.
   11756             :  * @since 3.4
   11757             :  */
   11758          12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
   11759             :                                    const char *pszVectorLayerName,
   11760             :                                    CSLConstList papszOptions)
   11761             : {
   11762          12 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11763          12 :     VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
   11764          24 :     return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
   11765          24 :         std::string(pszVectorLayerName), papszOptions));
   11766             : }
   11767             : 
   11768             : /************************************************************************/
   11769             : /*                       GDALGroupOpenMDArrayFromFullname()             */
   11770             : /************************************************************************/
   11771             : 
   11772             : /** Open and return a sub-group from its fully qualified name.
   11773             :  *
   11774             :  * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
   11775             :  *
   11776             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11777             :  *
   11778             :  * @since GDAL 3.2
   11779             :  */
   11780           3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
   11781             :                                           const char *pszFullname,
   11782             :                                           CSLConstList papszOptions)
   11783             : {
   11784           3 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11785           3 :     VALIDATE_POINTER1(pszFullname, __func__, nullptr);
   11786           3 :     auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
   11787           9 :         std::string(pszFullname), papszOptions);
   11788           3 :     if (!subGroup)
   11789           2 :         return nullptr;
   11790           1 :     return new GDALGroupHS(subGroup);
   11791             : }
   11792             : 
   11793             : /************************************************************************/
   11794             : /*                         GDALGroupGetDimensions()                     */
   11795             : /************************************************************************/
   11796             : 
   11797             : /** Return the list of dimensions contained in this group and used by its
   11798             :  * arrays.
   11799             :  *
   11800             :  * The returned array must be freed with GDALReleaseDimensions().  If only the
   11801             :  * array itself needs to be freed, CPLFree() should be called (and
   11802             :  * GDALDimensionRelease() on individual array members).
   11803             :  *
   11804             :  * This is the same as the C++ method GDALGroup::GetDimensions().
   11805             :  *
   11806             :  * @param hGroup Group.
   11807             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11808             :  * @param papszOptions Driver specific options determining how dimensions
   11809             :  * should be retrieved. Pass nullptr for default behavior.
   11810             :  *
   11811             :  * @return an array of *pnCount dimensions.
   11812             :  */
   11813          73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
   11814             :                                        CSLConstList papszOptions)
   11815             : {
   11816          73 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11817          73 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11818          73 :     auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
   11819             :     auto ret = static_cast<GDALDimensionH *>(
   11820          73 :         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
   11821         230 :     for (size_t i = 0; i < dims.size(); i++)
   11822             :     {
   11823         157 :         ret[i] = new GDALDimensionHS(dims[i]);
   11824             :     }
   11825          73 :     *pnCount = dims.size();
   11826          73 :     return ret;
   11827             : }
   11828             : 
   11829             : /************************************************************************/
   11830             : /*                          GDALGroupGetAttribute()                     */
   11831             : /************************************************************************/
   11832             : 
   11833             : /** Return an attribute by its name.
   11834             :  *
   11835             :  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
   11836             :  *
   11837             :  * The returned attribute must be freed with GDALAttributeRelease().
   11838             :  */
   11839          80 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
   11840             : {
   11841          80 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11842          80 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11843         240 :     auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
   11844          80 :     if (attr)
   11845          76 :         return new GDALAttributeHS(attr);
   11846           4 :     return nullptr;
   11847             : }
   11848             : 
   11849             : /************************************************************************/
   11850             : /*                         GDALGroupGetAttributes()                     */
   11851             : /************************************************************************/
   11852             : 
   11853             : /** Return the list of attributes contained in this group.
   11854             :  *
   11855             :  * The returned array must be freed with GDALReleaseAttributes(). If only the
   11856             :  * array itself needs to be freed, CPLFree() should be called (and
   11857             :  * GDALAttributeRelease() on individual array members).
   11858             :  *
   11859             :  * This is the same as the C++ method GDALGroup::GetAttributes().
   11860             :  *
   11861             :  * @param hGroup Group.
   11862             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11863             :  * @param papszOptions Driver specific options determining how attributes
   11864             :  * should be retrieved. Pass nullptr for default behavior.
   11865             :  *
   11866             :  * @return an array of *pnCount attributes.
   11867             :  */
   11868          71 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
   11869             :                                        CSLConstList papszOptions)
   11870             : {
   11871          71 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11872          71 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11873          71 :     auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
   11874             :     auto ret = static_cast<GDALAttributeH *>(
   11875          71 :         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
   11876         229 :     for (size_t i = 0; i < attrs.size(); i++)
   11877             :     {
   11878         158 :         ret[i] = new GDALAttributeHS(attrs[i]);
   11879             :     }
   11880          71 :     *pnCount = attrs.size();
   11881          71 :     return ret;
   11882             : }
   11883             : 
   11884             : /************************************************************************/
   11885             : /*                     GDALGroupGetStructuralInfo()                     */
   11886             : /************************************************************************/
   11887             : 
   11888             : /** Return structural information on the group.
   11889             :  *
   11890             :  * This may be the compression, etc..
   11891             :  *
   11892             :  * The return value should not be freed and is valid until GDALGroup is
   11893             :  * released or this function called again.
   11894             :  *
   11895             :  * This is the same as the C++ method GDALGroup::GetStructuralInfo().
   11896             :  */
   11897           4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
   11898             : {
   11899           4 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11900           4 :     return hGroup->m_poImpl->GetStructuralInfo();
   11901             : }
   11902             : 
   11903             : /************************************************************************/
   11904             : /*                   GDALGroupGetDataTypeCount()                        */
   11905             : /************************************************************************/
   11906             : 
   11907             : /** Return the number of data types associated with the group
   11908             :  * (typically enumerations).
   11909             :  *
   11910             :  * This is the same as the C++ method GDALGroup::GetDataTypes().size().
   11911             :  *
   11912             :  * @since 3.12
   11913             :  */
   11914           4 : size_t GDALGroupGetDataTypeCount(GDALGroupH hGroup)
   11915             : {
   11916           4 :     VALIDATE_POINTER1(hGroup, __func__, 0);
   11917           4 :     return hGroup->m_poImpl->GetDataTypes().size();
   11918             : }
   11919             : 
   11920             : /************************************************************************/
   11921             : /*                      GDALGroupGetDataType()                          */
   11922             : /************************************************************************/
   11923             : 
   11924             : /** Return one of the data types associated with the group.
   11925             :  *
   11926             :  * This is the same as the C++ method GDALGroup::GetDataTypes()[].
   11927             :  *
   11928             :  * @return a type to release with GDALExtendedDataTypeRelease() once done,
   11929             :  * or nullptr in case of error.
   11930             :  * @since 3.12
   11931             :  */
   11932           1 : GDALExtendedDataTypeH GDALGroupGetDataType(GDALGroupH hGroup, size_t nIdx)
   11933             : {
   11934           1 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11935           1 :     if (nIdx >= hGroup->m_poImpl->GetDataTypes().size())
   11936           0 :         return nullptr;
   11937           1 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   11938           1 :         *(hGroup->m_poImpl->GetDataTypes()[nIdx].get())));
   11939             : }
   11940             : 
   11941             : /************************************************************************/
   11942             : /*                         GDALReleaseAttributes()                      */
   11943             : /************************************************************************/
   11944             : 
   11945             : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
   11946             :  *
   11947             :  * @param attributes return pointer of above methods
   11948             :  * @param nCount *pnCount value returned by above methods
   11949             :  */
   11950         130 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
   11951             : {
   11952         418 :     for (size_t i = 0; i < nCount; i++)
   11953             :     {
   11954         288 :         delete attributes[i];
   11955             :     }
   11956         130 :     CPLFree(attributes);
   11957         130 : }
   11958             : 
   11959             : /************************************************************************/
   11960             : /*                         GDALGroupCreateGroup()                       */
   11961             : /************************************************************************/
   11962             : 
   11963             : /** Create a sub-group within a group.
   11964             :  *
   11965             :  * This is the same as the C++ method GDALGroup::CreateGroup().
   11966             :  *
   11967             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11968             :  */
   11969         179 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11970             :                                 CSLConstList papszOptions)
   11971             : {
   11972         179 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11973         179 :     VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
   11974         537 :     auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
   11975         537 :                                              papszOptions);
   11976         179 :     if (!ret)
   11977          49 :         return nullptr;
   11978         130 :     return new GDALGroupHS(ret);
   11979             : }
   11980             : 
   11981             : /************************************************************************/
   11982             : /*                         GDALGroupDeleteGroup()                       */
   11983             : /************************************************************************/
   11984             : 
   11985             : /** Delete a sub-group from a group.
   11986             :  *
   11987             :  * After this call, if a previously obtained instance of the deleted object
   11988             :  * is still alive, no method other than for freeing it should be invoked.
   11989             :  *
   11990             :  * This is the same as the C++ method GDALGroup::DeleteGroup().
   11991             :  *
   11992             :  * @return true in case of success.
   11993             :  * @since GDAL 3.8
   11994             :  */
   11995          20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11996             :                           CSLConstList papszOptions)
   11997             : {
   11998          20 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11999          20 :     VALIDATE_POINTER1(pszSubGroupName, __func__, false);
   12000          40 :     return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
   12001          20 :                                          papszOptions);
   12002             : }
   12003             : 
   12004             : /************************************************************************/
   12005             : /*                      GDALGroupCreateDimension()                      */
   12006             : /************************************************************************/
   12007             : 
   12008             : /** Create a dimension within a group.
   12009             :  *
   12010             :  * This is the same as the C++ method GDALGroup::CreateDimension().
   12011             :  *
   12012             :  * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
   12013             :  */
   12014         767 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
   12015             :                                         const char *pszType,
   12016             :                                         const char *pszDirection, GUInt64 nSize,
   12017             :                                         CSLConstList papszOptions)
   12018             : {
   12019         767 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12020         767 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   12021         767 :     auto ret = hGroup->m_poImpl->CreateDimension(
   12022        1534 :         std::string(pszName), std::string(pszType ? pszType : ""),
   12023        3068 :         std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
   12024         767 :     if (!ret)
   12025           9 :         return nullptr;
   12026         758 :     return new GDALDimensionHS(ret);
   12027             : }
   12028             : 
   12029             : /************************************************************************/
   12030             : /*                      GDALGroupCreateMDArray()                        */
   12031             : /************************************************************************/
   12032             : 
   12033             : /** Create a multidimensional array within a group.
   12034             :  *
   12035             :  * This is the same as the C++ method GDALGroup::CreateMDArray().
   12036             :  *
   12037             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   12038             :  */
   12039         709 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
   12040             :                                     size_t nDimensions,
   12041             :                                     GDALDimensionH *pahDimensions,
   12042             :                                     GDALExtendedDataTypeH hEDT,
   12043             :                                     CSLConstList papszOptions)
   12044             : {
   12045         709 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12046         709 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   12047         709 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   12048        1418 :     std::vector<std::shared_ptr<GDALDimension>> dims;
   12049         709 :     dims.reserve(nDimensions);
   12050        1692 :     for (size_t i = 0; i < nDimensions; i++)
   12051         983 :         dims.push_back(pahDimensions[i]->m_poImpl);
   12052        2127 :     auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
   12053        2127 :                                                *(hEDT->m_poImpl), papszOptions);
   12054         709 :     if (!ret)
   12055          64 :         return nullptr;
   12056         645 :     return new GDALMDArrayHS(ret);
   12057             : }
   12058             : 
   12059             : /************************************************************************/
   12060             : /*                         GDALGroupDeleteMDArray()                     */
   12061             : /************************************************************************/
   12062             : 
   12063             : /** Delete an array from a group.
   12064             :  *
   12065             :  * After this call, if a previously obtained instance of the deleted object
   12066             :  * is still alive, no method other than for freeing it should be invoked.
   12067             :  *
   12068             :  * This is the same as the C++ method GDALGroup::DeleteMDArray().
   12069             :  *
   12070             :  * @return true in case of success.
   12071             :  * @since GDAL 3.8
   12072             :  */
   12073          20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
   12074             :                             CSLConstList papszOptions)
   12075             : {
   12076          20 :     VALIDATE_POINTER1(hGroup, __func__, false);
   12077          20 :     VALIDATE_POINTER1(pszName, __func__, false);
   12078          20 :     return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
   12079             : }
   12080             : 
   12081             : /************************************************************************/
   12082             : /*                      GDALGroupCreateAttribute()                      */
   12083             : /************************************************************************/
   12084             : 
   12085             : /** Create a attribute within a group.
   12086             :  *
   12087             :  * This is the same as the C++ method GDALGroup::CreateAttribute().
   12088             :  *
   12089             :  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
   12090             :  */
   12091         125 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
   12092             :                                         size_t nDimensions,
   12093             :                                         const GUInt64 *panDimensions,
   12094             :                                         GDALExtendedDataTypeH hEDT,
   12095             :                                         CSLConstList papszOptions)
   12096             : {
   12097         125 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12098         125 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   12099         250 :     std::vector<GUInt64> dims;
   12100         125 :     dims.reserve(nDimensions);
   12101         175 :     for (size_t i = 0; i < nDimensions; i++)
   12102          50 :         dims.push_back(panDimensions[i]);
   12103         125 :     auto ret = hGroup->m_poImpl->CreateAttribute(
   12104         375 :         std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
   12105         125 :     if (!ret)
   12106          14 :         return nullptr;
   12107         111 :     return new GDALAttributeHS(ret);
   12108             : }
   12109             : 
   12110             : /************************************************************************/
   12111             : /*                         GDALGroupDeleteAttribute()                   */
   12112             : /************************************************************************/
   12113             : 
   12114             : /** Delete an attribute from a group.
   12115             :  *
   12116             :  * After this call, if a previously obtained instance of the deleted object
   12117             :  * is still alive, no method other than for freeing it should be invoked.
   12118             :  *
   12119             :  * This is the same as the C++ method GDALGroup::DeleteAttribute().
   12120             :  *
   12121             :  * @return true in case of success.
   12122             :  * @since GDAL 3.8
   12123             :  */
   12124          25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
   12125             :                               CSLConstList papszOptions)
   12126             : {
   12127          25 :     VALIDATE_POINTER1(hGroup, __func__, false);
   12128          25 :     VALIDATE_POINTER1(pszName, __func__, false);
   12129          50 :     return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
   12130          25 :                                              papszOptions);
   12131             : }
   12132             : 
   12133             : /************************************************************************/
   12134             : /*                          GDALGroupRename()                           */
   12135             : /************************************************************************/
   12136             : 
   12137             : /** Rename the group.
   12138             :  *
   12139             :  * This is not implemented by all drivers.
   12140             :  *
   12141             :  * Drivers known to implement it: MEM, netCDF.
   12142             :  *
   12143             :  * This is the same as the C++ method GDALGroup::Rename()
   12144             :  *
   12145             :  * @return true in case of success
   12146             :  * @since GDAL 3.8
   12147             :  */
   12148          45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
   12149             : {
   12150          45 :     VALIDATE_POINTER1(hGroup, __func__, false);
   12151          45 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   12152          45 :     return hGroup->m_poImpl->Rename(pszNewName);
   12153             : }
   12154             : 
   12155             : /************************************************************************/
   12156             : /*                 GDALGroupSubsetDimensionFromSelection()              */
   12157             : /************************************************************************/
   12158             : 
   12159             : /** Return a virtual group whose one dimension has been subset according to a
   12160             :  * selection.
   12161             :  *
   12162             :  * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
   12163             :  *
   12164             :  * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
   12165             :  */
   12166             : GDALGroupH
   12167          14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
   12168             :                                       const char *pszSelection,
   12169             :                                       CPL_UNUSED CSLConstList papszOptions)
   12170             : {
   12171          14 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   12172          14 :     VALIDATE_POINTER1(pszSelection, __func__, nullptr);
   12173          14 :     auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
   12174          42 :         std::string(pszSelection));
   12175          14 :     if (!hNewGroup)
   12176           8 :         return nullptr;
   12177           6 :     return new GDALGroupHS(hNewGroup);
   12178             : }
   12179             : 
   12180             : /************************************************************************/
   12181             : /*                        GDALMDArrayRelease()                          */
   12182             : /************************************************************************/
   12183             : 
   12184             : /** Release the GDAL in-memory object associated with a GDALMDArray.
   12185             :  *
   12186             :  * Note: when applied on a object coming from a driver, this does not
   12187             :  * destroy the object in the file, database, etc...
   12188             :  */
   12189        2171 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
   12190             : {
   12191        2171 :     delete hMDArray;
   12192        2171 : }
   12193             : 
   12194             : /************************************************************************/
   12195             : /*                        GDALMDArrayGetName()                          */
   12196             : /************************************************************************/
   12197             : 
   12198             : /** Return array name.
   12199             :  *
   12200             :  * This is the same as the C++ method GDALMDArray::GetName()
   12201             :  */
   12202          83 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
   12203             : {
   12204          83 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12205          83 :     return hArray->m_poImpl->GetName().c_str();
   12206             : }
   12207             : 
   12208             : /************************************************************************/
   12209             : /*                    GDALMDArrayGetFullName()                          */
   12210             : /************************************************************************/
   12211             : 
   12212             : /** Return array full name.
   12213             :  *
   12214             :  * This is the same as the C++ method GDALMDArray::GetFullName()
   12215             :  */
   12216          50 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
   12217             : {
   12218          50 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12219          50 :     return hArray->m_poImpl->GetFullName().c_str();
   12220             : }
   12221             : 
   12222             : /************************************************************************/
   12223             : /*                        GDALMDArrayGetName()                          */
   12224             : /************************************************************************/
   12225             : 
   12226             : /** Return the total number of values in the array.
   12227             :  *
   12228             :  * This is the same as the C++ method
   12229             :  * GDALAbstractMDArray::GetTotalElementsCount()
   12230             :  */
   12231           6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
   12232             : {
   12233           6 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12234           6 :     return hArray->m_poImpl->GetTotalElementsCount();
   12235             : }
   12236             : 
   12237             : /************************************************************************/
   12238             : /*                        GDALMDArrayGetDimensionCount()                */
   12239             : /************************************************************************/
   12240             : 
   12241             : /** Return the number of dimensions.
   12242             :  *
   12243             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
   12244             :  */
   12245       10821 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
   12246             : {
   12247       10821 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12248       10821 :     return hArray->m_poImpl->GetDimensionCount();
   12249             : }
   12250             : 
   12251             : /************************************************************************/
   12252             : /*                        GDALMDArrayGetDimensions()                    */
   12253             : /************************************************************************/
   12254             : 
   12255             : /** Return the dimensions of the array
   12256             :  *
   12257             :  * The returned array must be freed with GDALReleaseDimensions(). If only the
   12258             :  * array itself needs to be freed, CPLFree() should be called (and
   12259             :  * GDALDimensionRelease() on individual array members).
   12260             :  *
   12261             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
   12262             :  *
   12263             :  * @param hArray Array.
   12264             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12265             :  *
   12266             :  * @return an array of *pnCount dimensions.
   12267             :  */
   12268        2453 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
   12269             : {
   12270        2453 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12271        2453 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12272        2453 :     const auto &dims(hArray->m_poImpl->GetDimensions());
   12273             :     auto ret = static_cast<GDALDimensionH *>(
   12274        2453 :         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
   12275        6926 :     for (size_t i = 0; i < dims.size(); i++)
   12276             :     {
   12277        4473 :         ret[i] = new GDALDimensionHS(dims[i]);
   12278             :     }
   12279        2453 :     *pnCount = dims.size();
   12280        2453 :     return ret;
   12281             : }
   12282             : 
   12283             : /************************************************************************/
   12284             : /*                        GDALReleaseDimensions()                       */
   12285             : /************************************************************************/
   12286             : 
   12287             : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
   12288             :  *
   12289             :  * @param dims return pointer of above methods
   12290             :  * @param nCount *pnCount value returned by above methods
   12291             :  */
   12292        2526 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
   12293             : {
   12294        7156 :     for (size_t i = 0; i < nCount; i++)
   12295             :     {
   12296        4630 :         delete dims[i];
   12297             :     }
   12298        2526 :     CPLFree(dims);
   12299        2526 : }
   12300             : 
   12301             : /************************************************************************/
   12302             : /*                        GDALMDArrayGetDataType()                     */
   12303             : /************************************************************************/
   12304             : 
   12305             : /** Return the data type
   12306             :  *
   12307             :  * The return must be freed with GDALExtendedDataTypeRelease().
   12308             :  */
   12309        4182 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
   12310             : {
   12311        4182 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12312             :     return new GDALExtendedDataTypeHS(
   12313        4182 :         new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
   12314             : }
   12315             : 
   12316             : /************************************************************************/
   12317             : /*                          GDALMDArrayRead()                           */
   12318             : /************************************************************************/
   12319             : 
   12320             : /** Read part or totality of a multidimensional array.
   12321             :  *
   12322             :  * This is the same as the C++ method GDALAbstractMDArray::Read()
   12323             :  *
   12324             :  * @return TRUE in case of success.
   12325             :  */
   12326        1989 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   12327             :                     const size_t *count, const GInt64 *arrayStep,
   12328             :                     const GPtrDiff_t *bufferStride,
   12329             :                     GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
   12330             :                     const void *pDstBufferAllocStart,
   12331             :                     size_t nDstBufferAllocSize)
   12332             : {
   12333        1989 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12334        1989 :     if ((arrayStartIdx == nullptr || count == nullptr) &&
   12335           0 :         hArray->m_poImpl->GetDimensionCount() > 0)
   12336             :     {
   12337           0 :         VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
   12338           0 :         VALIDATE_POINTER1(count, __func__, FALSE);
   12339             :     }
   12340        1989 :     VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
   12341        1989 :     VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
   12342        3978 :     return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
   12343        1989 :                                   *(bufferDataType->m_poImpl), pDstBuffer,
   12344        1989 :                                   pDstBufferAllocStart, nDstBufferAllocSize);
   12345             : }
   12346             : 
   12347             : /************************************************************************/
   12348             : /*                          GDALMDArrayWrite()                           */
   12349             : /************************************************************************/
   12350             : 
   12351             : /** Write part or totality of a multidimensional array.
   12352             :  *
   12353             :  * This is the same as the C++ method GDALAbstractMDArray::Write()
   12354             :  *
   12355             :  * @return TRUE in case of success.
   12356             :  */
   12357         656 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   12358             :                      const size_t *count, const GInt64 *arrayStep,
   12359             :                      const GPtrDiff_t *bufferStride,
   12360             :                      GDALExtendedDataTypeH bufferDataType,
   12361             :                      const void *pSrcBuffer, const void *pSrcBufferAllocStart,
   12362             :                      size_t nSrcBufferAllocSize)
   12363             : {
   12364         656 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12365         656 :     if ((arrayStartIdx == nullptr || count == nullptr) &&
   12366           0 :         hArray->m_poImpl->GetDimensionCount() > 0)
   12367             :     {
   12368           0 :         VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
   12369           0 :         VALIDATE_POINTER1(count, __func__, FALSE);
   12370             :     }
   12371         656 :     VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
   12372         656 :     VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
   12373        1312 :     return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
   12374         656 :                                    bufferStride, *(bufferDataType->m_poImpl),
   12375             :                                    pSrcBuffer, pSrcBufferAllocStart,
   12376         656 :                                    nSrcBufferAllocSize);
   12377             : }
   12378             : 
   12379             : /************************************************************************/
   12380             : /*                       GDALMDArrayAdviseRead()                        */
   12381             : /************************************************************************/
   12382             : 
   12383             : /** Advise driver of upcoming read requests.
   12384             :  *
   12385             :  * This is the same as the C++ method GDALMDArray::AdviseRead()
   12386             :  *
   12387             :  * @return TRUE in case of success.
   12388             :  *
   12389             :  * @since GDAL 3.2
   12390             :  */
   12391           0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   12392             :                           const size_t *count)
   12393             : {
   12394           0 :     return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
   12395             : }
   12396             : 
   12397             : /************************************************************************/
   12398             : /*                      GDALMDArrayAdviseReadEx()                       */
   12399             : /************************************************************************/
   12400             : 
   12401             : /** Advise driver of upcoming read requests.
   12402             :  *
   12403             :  * This is the same as the C++ method GDALMDArray::AdviseRead()
   12404             :  *
   12405             :  * @return TRUE in case of success.
   12406             :  *
   12407             :  * @since GDAL 3.4
   12408             :  */
   12409          22 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   12410             :                             const size_t *count, CSLConstList papszOptions)
   12411             : {
   12412          22 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12413          22 :     return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
   12414             : }
   12415             : 
   12416             : /************************************************************************/
   12417             : /*                         GDALMDArrayGetAttribute()                    */
   12418             : /************************************************************************/
   12419             : 
   12420             : /** Return an attribute by its name.
   12421             :  *
   12422             :  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
   12423             :  *
   12424             :  * The returned attribute must be freed with GDALAttributeRelease().
   12425             :  */
   12426         120 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
   12427             : {
   12428         120 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12429         120 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   12430         360 :     auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
   12431         120 :     if (attr)
   12432         111 :         return new GDALAttributeHS(attr);
   12433           9 :     return nullptr;
   12434             : }
   12435             : 
   12436             : /************************************************************************/
   12437             : /*                        GDALMDArrayGetAttributes()                    */
   12438             : /************************************************************************/
   12439             : 
   12440             : /** Return the list of attributes contained in this array.
   12441             :  *
   12442             :  * The returned array must be freed with GDALReleaseAttributes(). If only the
   12443             :  * array itself needs to be freed, CPLFree() should be called (and
   12444             :  * GDALAttributeRelease() on individual array members).
   12445             :  *
   12446             :  * This is the same as the C++ method GDALMDArray::GetAttributes().
   12447             :  *
   12448             :  * @param hArray Array.
   12449             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12450             :  * @param papszOptions Driver specific options determining how attributes
   12451             :  * should be retrieved. Pass nullptr for default behavior.
   12452             :  *
   12453             :  * @return an array of *pnCount attributes.
   12454             :  */
   12455          59 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
   12456             :                                          CSLConstList papszOptions)
   12457             : {
   12458          59 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12459          59 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12460          59 :     auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
   12461             :     auto ret = static_cast<GDALAttributeH *>(
   12462          59 :         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
   12463         189 :     for (size_t i = 0; i < attrs.size(); i++)
   12464             :     {
   12465         130 :         ret[i] = new GDALAttributeHS(attrs[i]);
   12466             :     }
   12467          59 :     *pnCount = attrs.size();
   12468          59 :     return ret;
   12469             : }
   12470             : 
   12471             : /************************************************************************/
   12472             : /*                       GDALMDArrayCreateAttribute()                   */
   12473             : /************************************************************************/
   12474             : 
   12475             : /** Create a attribute within an array.
   12476             :  *
   12477             :  * This is the same as the C++ method GDALMDArray::CreateAttribute().
   12478             :  *
   12479             :  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
   12480             :  */
   12481         188 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
   12482             :                                           const char *pszName,
   12483             :                                           size_t nDimensions,
   12484             :                                           const GUInt64 *panDimensions,
   12485             :                                           GDALExtendedDataTypeH hEDT,
   12486             :                                           CSLConstList papszOptions)
   12487             : {
   12488         188 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12489         188 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   12490         188 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   12491         376 :     std::vector<GUInt64> dims;
   12492         188 :     dims.reserve(nDimensions);
   12493         249 :     for (size_t i = 0; i < nDimensions; i++)
   12494          61 :         dims.push_back(panDimensions[i]);
   12495         188 :     auto ret = hArray->m_poImpl->CreateAttribute(
   12496         564 :         std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
   12497         188 :     if (!ret)
   12498           9 :         return nullptr;
   12499         179 :     return new GDALAttributeHS(ret);
   12500             : }
   12501             : 
   12502             : /************************************************************************/
   12503             : /*                       GDALMDArrayDeleteAttribute()                   */
   12504             : /************************************************************************/
   12505             : 
   12506             : /** Delete an attribute from an array.
   12507             :  *
   12508             :  * After this call, if a previously obtained instance of the deleted object
   12509             :  * is still alive, no method other than for freeing it should be invoked.
   12510             :  *
   12511             :  * This is the same as the C++ method GDALMDArray::DeleteAttribute().
   12512             :  *
   12513             :  * @return true in case of success.
   12514             :  * @since GDAL 3.8
   12515             :  */
   12516          24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
   12517             :                                 CSLConstList papszOptions)
   12518             : {
   12519          24 :     VALIDATE_POINTER1(hArray, __func__, false);
   12520          24 :     VALIDATE_POINTER1(pszName, __func__, false);
   12521          48 :     return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
   12522          24 :                                              papszOptions);
   12523             : }
   12524             : 
   12525             : /************************************************************************/
   12526             : /*                       GDALMDArrayGetRawNoDataValue()                 */
   12527             : /************************************************************************/
   12528             : 
   12529             : /** Return the nodata value as a "raw" value.
   12530             :  *
   12531             :  * The value returned might be nullptr in case of no nodata value. When
   12532             :  * a nodata value is registered, a non-nullptr will be returned whose size in
   12533             :  * bytes is GetDataType().GetSize().
   12534             :  *
   12535             :  * The returned value should not be modified or freed.
   12536             :  *
   12537             :  * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
   12538             :  *
   12539             :  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
   12540             :  */
   12541          77 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
   12542             : {
   12543          77 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12544          77 :     return hArray->m_poImpl->GetRawNoDataValue();
   12545             : }
   12546             : 
   12547             : /************************************************************************/
   12548             : /*                      GDALMDArrayGetNoDataValueAsDouble()             */
   12549             : /************************************************************************/
   12550             : 
   12551             : /** Return the nodata value as a double.
   12552             :  *
   12553             :  * The value returned might be nullptr in case of no nodata value. When
   12554             :  * a nodata value is registered, a non-nullptr will be returned whose size in
   12555             :  * bytes is GetDataType().GetSize().
   12556             :  *
   12557             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
   12558             :  *
   12559             :  * @param hArray Array handle.
   12560             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12561             :  * if a nodata value exists and can be converted to double. Might be nullptr.
   12562             :  *
   12563             :  * @return the nodata value as a double. A 0.0 value might also indicate the
   12564             :  * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
   12565             :  * will be set to false then).
   12566             :  */
   12567         121 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
   12568             :                                          int *pbHasNoDataValue)
   12569             : {
   12570         121 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12571         121 :     bool bHasNodataValue = false;
   12572         121 :     double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
   12573         121 :     if (pbHasNoDataValue)
   12574         121 :         *pbHasNoDataValue = bHasNodataValue;
   12575         121 :     return ret;
   12576             : }
   12577             : 
   12578             : /************************************************************************/
   12579             : /*                      GDALMDArrayGetNoDataValueAsInt64()              */
   12580             : /************************************************************************/
   12581             : 
   12582             : /** Return the nodata value as a Int64.
   12583             :  *
   12584             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
   12585             :  *
   12586             :  * @param hArray Array handle.
   12587             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12588             :  * if a nodata value exists and can be converted to Int64. Might be nullptr.
   12589             :  *
   12590             :  * @return the nodata value as a Int64.
   12591             :  * @since GDAL 3.5
   12592             :  */
   12593          11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
   12594             :                                          int *pbHasNoDataValue)
   12595             : {
   12596          11 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12597          11 :     bool bHasNodataValue = false;
   12598          11 :     const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
   12599          11 :     if (pbHasNoDataValue)
   12600          11 :         *pbHasNoDataValue = bHasNodataValue;
   12601          11 :     return ret;
   12602             : }
   12603             : 
   12604             : /************************************************************************/
   12605             : /*                      GDALMDArrayGetNoDataValueAsUInt64()              */
   12606             : /************************************************************************/
   12607             : 
   12608             : /** Return the nodata value as a UInt64.
   12609             :  *
   12610             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
   12611             :  *
   12612             :  * @param hArray Array handle.
   12613             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12614             :  * if a nodata value exists and can be converted to UInt64. Might be nullptr.
   12615             :  *
   12616             :  * @return the nodata value as a UInt64.
   12617             :  * @since GDAL 3.5
   12618             :  */
   12619           7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
   12620             :                                            int *pbHasNoDataValue)
   12621             : {
   12622           7 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12623           7 :     bool bHasNodataValue = false;
   12624           7 :     const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
   12625           7 :     if (pbHasNoDataValue)
   12626           7 :         *pbHasNoDataValue = bHasNodataValue;
   12627           7 :     return ret;
   12628             : }
   12629             : 
   12630             : /************************************************************************/
   12631             : /*                     GDALMDArraySetRawNoDataValue()                   */
   12632             : /************************************************************************/
   12633             : 
   12634             : /** Set the nodata value as a "raw" value.
   12635             :  *
   12636             :  * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
   12637             :  * void*).
   12638             :  *
   12639             :  * @return TRUE in case of success.
   12640             :  */
   12641          14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
   12642             : {
   12643          14 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12644          14 :     return hArray->m_poImpl->SetRawNoDataValue(pNoData);
   12645             : }
   12646             : 
   12647             : /************************************************************************/
   12648             : /*                   GDALMDArraySetNoDataValueAsDouble()                */
   12649             : /************************************************************************/
   12650             : 
   12651             : /** Set the nodata value as a double.
   12652             :  *
   12653             :  * If the natural data type of the attribute/array is not double, type
   12654             :  * conversion will occur to the type returned by GetDataType().
   12655             :  *
   12656             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
   12657             :  *
   12658             :  * @return TRUE in case of success.
   12659             :  */
   12660          55 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
   12661             : {
   12662          55 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12663          55 :     return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
   12664             : }
   12665             : 
   12666             : /************************************************************************/
   12667             : /*                   GDALMDArraySetNoDataValueAsInt64()                 */
   12668             : /************************************************************************/
   12669             : 
   12670             : /** Set the nodata value as a Int64.
   12671             :  *
   12672             :  * If the natural data type of the attribute/array is not Int64, type conversion
   12673             :  * will occur to the type returned by GetDataType().
   12674             :  *
   12675             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
   12676             :  *
   12677             :  * @return TRUE in case of success.
   12678             :  * @since GDAL 3.5
   12679             :  */
   12680           1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
   12681             : {
   12682           1 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12683           1 :     return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
   12684             : }
   12685             : 
   12686             : /************************************************************************/
   12687             : /*                   GDALMDArraySetNoDataValueAsUInt64()                */
   12688             : /************************************************************************/
   12689             : 
   12690             : /** Set the nodata value as a UInt64.
   12691             :  *
   12692             :  * If the natural data type of the attribute/array is not UInt64, type
   12693             :  * conversion will occur to the type returned by GetDataType().
   12694             :  *
   12695             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
   12696             :  *
   12697             :  * @return TRUE in case of success.
   12698             :  * @since GDAL 3.5
   12699             :  */
   12700           1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
   12701             :                                       uint64_t nNoDataValue)
   12702             : {
   12703           1 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12704           1 :     return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
   12705             : }
   12706             : 
   12707             : /************************************************************************/
   12708             : /*                        GDALMDArrayResize()                           */
   12709             : /************************************************************************/
   12710             : 
   12711             : /** Resize an array to new dimensions.
   12712             :  *
   12713             :  * Not all drivers may allow this operation, and with restrictions (e.g.
   12714             :  * for netCDF, this is limited to growing of "unlimited" dimensions)
   12715             :  *
   12716             :  * Resizing a dimension used in other arrays will cause those other arrays
   12717             :  * to be resized.
   12718             :  *
   12719             :  * This is the same as the C++ method GDALMDArray::Resize().
   12720             :  *
   12721             :  * @param hArray Array.
   12722             :  * @param panNewDimSizes Array of GetDimensionCount() values containing the
   12723             :  *                       new size of each indexing dimension.
   12724             :  * @param papszOptions Options. (Driver specific)
   12725             :  * @return true in case of success.
   12726             :  * @since GDAL 3.7
   12727             :  */
   12728          42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
   12729             :                        CSLConstList papszOptions)
   12730             : {
   12731          42 :     VALIDATE_POINTER1(hArray, __func__, false);
   12732          42 :     VALIDATE_POINTER1(panNewDimSizes, __func__, false);
   12733          84 :     std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
   12734         125 :     for (size_t i = 0; i < anNewDimSizes.size(); ++i)
   12735             :     {
   12736          83 :         anNewDimSizes[i] = panNewDimSizes[i];
   12737             :     }
   12738          42 :     return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
   12739             : }
   12740             : 
   12741             : /************************************************************************/
   12742             : /*                          GDALMDArraySetScale()                       */
   12743             : /************************************************************************/
   12744             : 
   12745             : /** Set the scale value to apply to raw values.
   12746             :  *
   12747             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12748             :  *
   12749             :  * This is the same as the C++ method GDALMDArray::SetScale().
   12750             :  *
   12751             :  * @return TRUE in case of success.
   12752             :  */
   12753           0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
   12754             : {
   12755           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12756           0 :     return hArray->m_poImpl->SetScale(dfScale);
   12757             : }
   12758             : 
   12759             : /************************************************************************/
   12760             : /*                        GDALMDArraySetScaleEx()                       */
   12761             : /************************************************************************/
   12762             : 
   12763             : /** Set the scale value to apply to raw values.
   12764             :  *
   12765             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12766             :  *
   12767             :  * This is the same as the C++ method GDALMDArray::SetScale().
   12768             :  *
   12769             :  * @return TRUE in case of success.
   12770             :  * @since GDAL 3.3
   12771             :  */
   12772          21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
   12773             :                           GDALDataType eStorageType)
   12774             : {
   12775          21 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12776          21 :     return hArray->m_poImpl->SetScale(dfScale, eStorageType);
   12777             : }
   12778             : 
   12779             : /************************************************************************/
   12780             : /*                          GDALMDArraySetOffset()                       */
   12781             : /************************************************************************/
   12782             : 
   12783             : /** Set the scale value to apply to raw values.
   12784             :  *
   12785             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12786             :  *
   12787             :  * This is the same as the C++ method GDALMDArray::SetOffset().
   12788             :  *
   12789             :  * @return TRUE in case of success.
   12790             :  */
   12791           0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
   12792             : {
   12793           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12794           0 :     return hArray->m_poImpl->SetOffset(dfOffset);
   12795             : }
   12796             : 
   12797             : /************************************************************************/
   12798             : /*                       GDALMDArraySetOffsetEx()                       */
   12799             : /************************************************************************/
   12800             : 
   12801             : /** Set the scale value to apply to raw values.
   12802             :  *
   12803             :  * unscaled_value = raw_value * GetOffset() + GetOffset()
   12804             :  *
   12805             :  * This is the same as the C++ method GDALMDArray::SetOffset().
   12806             :  *
   12807             :  * @return TRUE in case of success.
   12808             :  * @since GDAL 3.3
   12809             :  */
   12810          21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
   12811             :                            GDALDataType eStorageType)
   12812             : {
   12813          21 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12814          21 :     return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
   12815             : }
   12816             : 
   12817             : /************************************************************************/
   12818             : /*                          GDALMDArrayGetScale()                       */
   12819             : /************************************************************************/
   12820             : 
   12821             : /** Get the scale value to apply to raw values.
   12822             :  *
   12823             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12824             :  *
   12825             :  * This is the same as the C++ method GDALMDArray::GetScale().
   12826             :  *
   12827             :  * @return the scale value
   12828             :  */
   12829         105 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
   12830             : {
   12831         105 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12832         105 :     bool bHasValue = false;
   12833         105 :     double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
   12834         105 :     if (pbHasValue)
   12835         105 :         *pbHasValue = bHasValue;
   12836         105 :     return dfRet;
   12837             : }
   12838             : 
   12839             : /************************************************************************/
   12840             : /*                        GDALMDArrayGetScaleEx()                       */
   12841             : /************************************************************************/
   12842             : 
   12843             : /** Get the scale value to apply to raw values.
   12844             :  *
   12845             :  * unscaled_value = raw_value * GetScale() + GetScale()
   12846             :  *
   12847             :  * This is the same as the C++ method GDALMDArray::GetScale().
   12848             :  *
   12849             :  * @return the scale value
   12850             :  * @since GDAL 3.3
   12851             :  */
   12852           5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
   12853             :                              GDALDataType *peStorageType)
   12854             : {
   12855           5 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12856           5 :     bool bHasValue = false;
   12857           5 :     double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
   12858           5 :     if (pbHasValue)
   12859           5 :         *pbHasValue = bHasValue;
   12860           5 :     return dfRet;
   12861             : }
   12862             : 
   12863             : /************************************************************************/
   12864             : /*                          GDALMDArrayGetOffset()                      */
   12865             : /************************************************************************/
   12866             : 
   12867             : /** Get the scale value to apply to raw values.
   12868             :  *
   12869             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12870             :  *
   12871             :  * This is the same as the C++ method GDALMDArray::GetOffset().
   12872             :  *
   12873             :  * @return the scale value
   12874             :  */
   12875         102 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
   12876             : {
   12877         102 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12878         102 :     bool bHasValue = false;
   12879         102 :     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
   12880         102 :     if (pbHasValue)
   12881         102 :         *pbHasValue = bHasValue;
   12882         102 :     return dfRet;
   12883             : }
   12884             : 
   12885             : /************************************************************************/
   12886             : /*                        GDALMDArrayGetOffsetEx()                      */
   12887             : /************************************************************************/
   12888             : 
   12889             : /** Get the scale value to apply to raw values.
   12890             :  *
   12891             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12892             :  *
   12893             :  * This is the same as the C++ method GDALMDArray::GetOffset().
   12894             :  *
   12895             :  * @return the scale value
   12896             :  * @since GDAL 3.3
   12897             :  */
   12898           5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
   12899             :                               GDALDataType *peStorageType)
   12900             : {
   12901           5 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12902           5 :     bool bHasValue = false;
   12903           5 :     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
   12904           5 :     if (pbHasValue)
   12905           5 :         *pbHasValue = bHasValue;
   12906           5 :     return dfRet;
   12907             : }
   12908             : 
   12909             : /************************************************************************/
   12910             : /*                      GDALMDArrayGetBlockSize()                       */
   12911             : /************************************************************************/
   12912             : 
   12913             : /** Return the "natural" block size of the array along all dimensions.
   12914             :  *
   12915             :  * Some drivers might organize the array in tiles/blocks and reading/writing
   12916             :  * aligned on those tile/block boundaries will be more efficient.
   12917             :  *
   12918             :  * The returned number of elements in the vector is the same as
   12919             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
   12920             :  * the natural block size along the considered dimension.
   12921             :  * "Flat" arrays will typically return a vector of values set to 0.
   12922             :  *
   12923             :  * The default implementation will return a vector of values set to 0.
   12924             :  *
   12925             :  * This method is used by GetProcessingChunkSize().
   12926             :  *
   12927             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
   12928             :  * theoretical case of a 32-bit platform, this might exceed its size_t
   12929             :  * allocation capabilities.
   12930             :  *
   12931             :  * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
   12932             :  *
   12933             :  * @return the block size, in number of elements along each dimension.
   12934             :  */
   12935          98 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
   12936             : {
   12937          98 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12938          98 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12939          98 :     auto res = hArray->m_poImpl->GetBlockSize();
   12940          98 :     auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
   12941         301 :     for (size_t i = 0; i < res.size(); i++)
   12942             :     {
   12943         203 :         ret[i] = res[i];
   12944             :     }
   12945          98 :     *pnCount = res.size();
   12946          98 :     return ret;
   12947             : }
   12948             : 
   12949             : /***********************************************************************/
   12950             : /*                   GDALMDArrayGetProcessingChunkSize()               */
   12951             : /************************************************************************/
   12952             : 
   12953             : /** \brief Return an optimal chunk size for read/write operations, given the
   12954             :  * natural block size and memory constraints specified.
   12955             :  *
   12956             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
   12957             :  * multiple of those returned by GetBlockSize() (unless the block define by
   12958             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
   12959             :  * returned by this method).
   12960             :  *
   12961             :  * This is the same as the C++ method
   12962             :  * GDALAbstractMDArray::GetProcessingChunkSize().
   12963             :  *
   12964             :  * @param hArray Array.
   12965             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12966             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
   12967             :  * chunk.
   12968             :  *
   12969             :  * @return the chunk size, in number of elements along each dimension.
   12970             :  */
   12971             : 
   12972           1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
   12973             :                                           size_t nMaxChunkMemory)
   12974             : {
   12975           1 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12976           1 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12977           1 :     auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
   12978           1 :     auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
   12979           3 :     for (size_t i = 0; i < res.size(); i++)
   12980             :     {
   12981           2 :         ret[i] = res[i];
   12982             :     }
   12983           1 :     *pnCount = res.size();
   12984           1 :     return ret;
   12985             : }
   12986             : 
   12987             : /************************************************************************/
   12988             : /*                     GDALMDArrayGetStructuralInfo()                   */
   12989             : /************************************************************************/
   12990             : 
   12991             : /** Return structural information on the array.
   12992             :  *
   12993             :  * This may be the compression, etc..
   12994             :  *
   12995             :  * The return value should not be freed and is valid until GDALMDArray is
   12996             :  * released or this function called again.
   12997             :  *
   12998             :  * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
   12999             :  */
   13000          15 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
   13001             : {
   13002          15 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13003          15 :     return hArray->m_poImpl->GetStructuralInfo();
   13004             : }
   13005             : 
   13006             : /************************************************************************/
   13007             : /*                        GDALMDArrayGetView()                          */
   13008             : /************************************************************************/
   13009             : 
   13010             : /** Return a view of the array using slicing or field access.
   13011             :  *
   13012             :  * The returned object should be released with GDALMDArrayRelease().
   13013             :  *
   13014             :  * This is the same as the C++ method GDALMDArray::GetView().
   13015             :  */
   13016         433 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
   13017             : {
   13018         433 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13019         433 :     VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
   13020        1299 :     auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
   13021         433 :     if (!sliced)
   13022          22 :         return nullptr;
   13023         411 :     return new GDALMDArrayHS(sliced);
   13024             : }
   13025             : 
   13026             : /************************************************************************/
   13027             : /*                       GDALMDArrayTranspose()                         */
   13028             : /************************************************************************/
   13029             : 
   13030             : /** Return a view of the array whose axis have been reordered.
   13031             :  *
   13032             :  * The returned object should be released with GDALMDArrayRelease().
   13033             :  *
   13034             :  * This is the same as the C++ method GDALMDArray::Transpose().
   13035             :  */
   13036          44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
   13037             :                                   const int *panMapNewAxisToOldAxis)
   13038             : {
   13039          44 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13040          88 :     std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
   13041          44 :     if (nNewAxisCount)
   13042             :     {
   13043          43 :         memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
   13044             :                nNewAxisCount * sizeof(int));
   13045             :     }
   13046          88 :     auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
   13047          44 :     if (!reordered)
   13048           7 :         return nullptr;
   13049          37 :     return new GDALMDArrayHS(reordered);
   13050             : }
   13051             : 
   13052             : /************************************************************************/
   13053             : /*                      GDALMDArrayGetUnscaled()                        */
   13054             : /************************************************************************/
   13055             : 
   13056             : /** Return an array that is the unscaled version of the current one.
   13057             :  *
   13058             :  * That is each value of the unscaled array will be
   13059             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   13060             :  *
   13061             :  * Starting with GDAL 3.3, the Write() method is implemented and will convert
   13062             :  * from unscaled values to raw values.
   13063             :  *
   13064             :  * The returned object should be released with GDALMDArrayRelease().
   13065             :  *
   13066             :  * This is the same as the C++ method GDALMDArray::GetUnscaled().
   13067             :  */
   13068          13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
   13069             : {
   13070          13 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13071          26 :     auto unscaled = hArray->m_poImpl->GetUnscaled();
   13072          13 :     if (!unscaled)
   13073           0 :         return nullptr;
   13074          13 :     return new GDALMDArrayHS(unscaled);
   13075             : }
   13076             : 
   13077             : /************************************************************************/
   13078             : /*                          GDALMDArrayGetMask()                         */
   13079             : /************************************************************************/
   13080             : 
   13081             : /** Return an array that is a mask for the current array
   13082             :  *
   13083             :  * This array will be of type Byte, with values set to 0 to indicate invalid
   13084             :  * pixels of the current array, and values set to 1 to indicate valid pixels.
   13085             :  *
   13086             :  * The returned object should be released with GDALMDArrayRelease().
   13087             :  *
   13088             :  * This is the same as the C++ method GDALMDArray::GetMask().
   13089             :  */
   13090          35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
   13091             : {
   13092          35 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13093          70 :     auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
   13094          35 :     if (!unscaled)
   13095           7 :         return nullptr;
   13096          28 :     return new GDALMDArrayHS(unscaled);
   13097             : }
   13098             : 
   13099             : /************************************************************************/
   13100             : /*                   GDALMDArrayGetResampled()                          */
   13101             : /************************************************************************/
   13102             : 
   13103             : /** Return an array that is a resampled / reprojected view of the current array
   13104             :  *
   13105             :  * This is the same as the C++ method GDALMDArray::GetResampled().
   13106             :  *
   13107             :  * Currently this method can only resample along the last 2 dimensions, unless
   13108             :  * orthorectifying a NASA EMIT dataset.
   13109             :  *
   13110             :  * The returned object should be released with GDALMDArrayRelease().
   13111             :  *
   13112             :  * @since 3.4
   13113             :  */
   13114          34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
   13115             :                                      const GDALDimensionH *pahNewDims,
   13116             :                                      GDALRIOResampleAlg resampleAlg,
   13117             :                                      OGRSpatialReferenceH hTargetSRS,
   13118             :                                      CSLConstList papszOptions)
   13119             : {
   13120          34 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13121          34 :     VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
   13122          68 :     std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
   13123         112 :     for (size_t i = 0; i < nNewDimCount; ++i)
   13124             :     {
   13125          78 :         if (pahNewDims[i])
   13126           8 :             apoNewDims[i] = pahNewDims[i]->m_poImpl;
   13127             :     }
   13128          34 :     auto poNewArray = hArray->m_poImpl->GetResampled(
   13129          34 :         apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
   13130          68 :         papszOptions);
   13131          34 :     if (!poNewArray)
   13132           8 :         return nullptr;
   13133          26 :     return new GDALMDArrayHS(poNewArray);
   13134             : }
   13135             : 
   13136             : /************************************************************************/
   13137             : /*                      GDALMDArraySetUnit()                            */
   13138             : /************************************************************************/
   13139             : 
   13140             : /** Set the variable unit.
   13141             :  *
   13142             :  * Values should conform as much as possible with those allowed by
   13143             :  * the NetCDF CF conventions:
   13144             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
   13145             :  * but others might be returned.
   13146             :  *
   13147             :  * Few examples are "meter", "degrees", "second", ...
   13148             :  * Empty value means unknown.
   13149             :  *
   13150             :  * This is the same as the C function GDALMDArraySetUnit()
   13151             :  *
   13152             :  * @param hArray array.
   13153             :  * @param pszUnit unit name.
   13154             :  * @return TRUE in case of success.
   13155             :  */
   13156          15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
   13157             : {
   13158          15 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13159          15 :     return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
   13160             : }
   13161             : 
   13162             : /************************************************************************/
   13163             : /*                      GDALMDArrayGetUnit()                            */
   13164             : /************************************************************************/
   13165             : 
   13166             : /** Return the array unit.
   13167             :  *
   13168             :  * Values should conform as much as possible with those allowed by
   13169             :  * the NetCDF CF conventions:
   13170             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
   13171             :  * but others might be returned.
   13172             :  *
   13173             :  * Few examples are "meter", "degrees", "second", ...
   13174             :  * Empty value means unknown.
   13175             :  *
   13176             :  * The return value should not be freed and is valid until GDALMDArray is
   13177             :  * released or this function called again.
   13178             :  *
   13179             :  * This is the same as the C++ method GDALMDArray::GetUnit().
   13180             :  */
   13181         113 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
   13182             : {
   13183         113 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13184         113 :     return hArray->m_poImpl->GetUnit().c_str();
   13185             : }
   13186             : 
   13187             : /************************************************************************/
   13188             : /*                      GDALMDArrayGetSpatialRef()                      */
   13189             : /************************************************************************/
   13190             : 
   13191             : /** Assign a spatial reference system object to the array.
   13192             :  *
   13193             :  * This is the same as the C++ method GDALMDArray::SetSpatialRef().
   13194             :  * @return TRUE in case of success.
   13195             :  */
   13196          30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
   13197             : {
   13198          30 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13199          60 :     return hArray->m_poImpl->SetSpatialRef(
   13200          60 :         OGRSpatialReference::FromHandle(hSRS));
   13201             : }
   13202             : 
   13203             : /************************************************************************/
   13204             : /*                      GDALMDArrayGetSpatialRef()                      */
   13205             : /************************************************************************/
   13206             : 
   13207             : /** Return the spatial reference system object associated with the array.
   13208             :  *
   13209             :  * This is the same as the C++ method GDALMDArray::GetSpatialRef().
   13210             :  *
   13211             :  * The returned object must be freed with OSRDestroySpatialReference().
   13212             :  */
   13213          81 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
   13214             : {
   13215          81 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13216          81 :     auto poSRS = hArray->m_poImpl->GetSpatialRef();
   13217          81 :     return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
   13218             : }
   13219             : 
   13220             : /************************************************************************/
   13221             : /*                      GDALMDArrayGetStatistics()                      */
   13222             : /************************************************************************/
   13223             : 
   13224             : /**
   13225             :  * \brief Fetch statistics.
   13226             :  *
   13227             :  * This is the same as the C++ method GDALMDArray::GetStatistics().
   13228             :  *
   13229             :  * @since GDAL 3.2
   13230             :  */
   13231             : 
   13232          15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
   13233             :                                 int bApproxOK, int bForce, double *pdfMin,
   13234             :                                 double *pdfMax, double *pdfMean,
   13235             :                                 double *pdfStdDev, GUInt64 *pnValidCount,
   13236             :                                 GDALProgressFunc pfnProgress,
   13237             :                                 void *pProgressData)
   13238             : {
   13239          15 :     VALIDATE_POINTER1(hArray, __func__, CE_Failure);
   13240          30 :     return hArray->m_poImpl->GetStatistics(
   13241          15 :         CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
   13242          15 :         pdfStdDev, pnValidCount, pfnProgress, pProgressData);
   13243             : }
   13244             : 
   13245             : /************************************************************************/
   13246             : /*                      GDALMDArrayComputeStatistics()                  */
   13247             : /************************************************************************/
   13248             : 
   13249             : /**
   13250             :  * \brief Compute statistics.
   13251             :  *
   13252             :  * This is the same as the C++ method GDALMDArray::ComputeStatistics().
   13253             :  *
   13254             :  * @since GDAL 3.2
   13255             :  * @see GDALMDArrayComputeStatisticsEx()
   13256             :  */
   13257             : 
   13258           0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
   13259             :                                  int bApproxOK, double *pdfMin, double *pdfMax,
   13260             :                                  double *pdfMean, double *pdfStdDev,
   13261             :                                  GUInt64 *pnValidCount,
   13262             :                                  GDALProgressFunc pfnProgress,
   13263             :                                  void *pProgressData)
   13264             : {
   13265           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13266           0 :     return hArray->m_poImpl->ComputeStatistics(
   13267           0 :         CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
   13268           0 :         pnValidCount, pfnProgress, pProgressData, nullptr);
   13269             : }
   13270             : 
   13271             : /************************************************************************/
   13272             : /*                     GDALMDArrayComputeStatisticsEx()                 */
   13273             : /************************************************************************/
   13274             : 
   13275             : /**
   13276             :  * \brief Compute statistics.
   13277             :  *
   13278             :  * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
   13279             :  *
   13280             :  * This is the same as the C++ method GDALMDArray::ComputeStatistics().
   13281             :  *
   13282             :  * @since GDAL 3.8
   13283             :  */
   13284             : 
   13285           4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
   13286             :                                    int bApproxOK, double *pdfMin,
   13287             :                                    double *pdfMax, double *pdfMean,
   13288             :                                    double *pdfStdDev, GUInt64 *pnValidCount,
   13289             :                                    GDALProgressFunc pfnProgress,
   13290             :                                    void *pProgressData,
   13291             :                                    CSLConstList papszOptions)
   13292             : {
   13293           4 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13294           8 :     return hArray->m_poImpl->ComputeStatistics(
   13295           4 :         CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
   13296           8 :         pnValidCount, pfnProgress, pProgressData, papszOptions);
   13297             : }
   13298             : 
   13299             : /************************************************************************/
   13300             : /*                 GDALMDArrayGetCoordinateVariables()                  */
   13301             : /************************************************************************/
   13302             : 
   13303             : /** Return coordinate variables.
   13304             :  *
   13305             :  * The returned array must be freed with GDALReleaseArrays(). If only the array
   13306             :  * itself needs to be freed, CPLFree() should be called (and
   13307             :  * GDALMDArrayRelease() on individual array members).
   13308             :  *
   13309             :  * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
   13310             :  *
   13311             :  * @param hArray Array.
   13312             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13313             :  *
   13314             :  * @return an array of *pnCount arrays.
   13315             :  * @since 3.4
   13316             :  */
   13317          13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
   13318             :                                                 size_t *pnCount)
   13319             : {
   13320          13 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13321          13 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13322          13 :     const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
   13323             :     auto ret = static_cast<GDALMDArrayH *>(
   13324          13 :         CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
   13325          29 :     for (size_t i = 0; i < coordinates.size(); i++)
   13326             :     {
   13327          16 :         ret[i] = new GDALMDArrayHS(coordinates[i]);
   13328             :     }
   13329          13 :     *pnCount = coordinates.size();
   13330          13 :     return ret;
   13331             : }
   13332             : 
   13333             : /************************************************************************/
   13334             : /*                     GDALMDArrayGetGridded()                          */
   13335             : /************************************************************************/
   13336             : 
   13337             : /** Return a gridded array from scattered point data, that is from an array
   13338             :  * whose last dimension is the indexing variable of X and Y arrays.
   13339             :  *
   13340             :  * The returned object should be released with GDALMDArrayRelease().
   13341             :  *
   13342             :  * This is the same as the C++ method GDALMDArray::GetGridded().
   13343             :  *
   13344             :  * @since GDAL 3.7
   13345             :  */
   13346          22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
   13347             :                                    const char *pszGridOptions,
   13348             :                                    GDALMDArrayH hXArray, GDALMDArrayH hYArray,
   13349             :                                    CSLConstList papszOptions)
   13350             : {
   13351          22 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13352          22 :     VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
   13353          22 :     auto gridded = hArray->m_poImpl->GetGridded(
   13354          44 :         pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
   13355          88 :         hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
   13356          22 :     if (!gridded)
   13357          19 :         return nullptr;
   13358           3 :     return new GDALMDArrayHS(gridded);
   13359             : }
   13360             : 
   13361             : /************************************************************************/
   13362             : /*                      GDALMDArrayGetMeshGrid()                        */
   13363             : /************************************************************************/
   13364             : 
   13365             : /** Return a list of multidimensional arrays from a list of one-dimensional
   13366             :  * arrays.
   13367             :  *
   13368             :  * This is typically used to transform one-dimensional longitude, latitude
   13369             :  * arrays into 2D ones.
   13370             :  *
   13371             :  * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
   13372             :  * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
   13373             :  * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
   13374             :  * repeated to fill the matrix along the first dimension for x1, the second
   13375             :  * for x2 and so on.
   13376             :  *
   13377             :  * For example, if x = [1, 2], and y = [3, 4, 5],
   13378             :  * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
   13379             :  * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
   13380             :  * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
   13381             :  *
   13382             :  * and
   13383             :  * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
   13384             :  * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
   13385             :  * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
   13386             :  *
   13387             :  * The currently supported options are:
   13388             :  * <ul>
   13389             :  * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
   13390             :  * output.
   13391             :  * </li>
   13392             :  * </ul>
   13393             :  *
   13394             :  * This is the same as
   13395             :  * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
   13396             :  * function.
   13397             :  *
   13398             :  * The returned array (of arrays) must be freed with GDALReleaseArrays().
   13399             :  * If only the array itself needs to be freed, CPLFree() should be called
   13400             :  * (and GDALMDArrayRelease() on individual array members).
   13401             :  *
   13402             :  * This is the same as the C++ method GDALMDArray::GetMeshGrid()
   13403             :  *
   13404             :  * @param pahInputArrays Input arrays
   13405             :  * @param nCountInputArrays Number of input arrays
   13406             :  * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
   13407             :  * @param papszOptions NULL, or NULL terminated list of options.
   13408             :  *
   13409             :  * @return an array of *pnCountOutputArrays arrays.
   13410             :  * @since 3.10
   13411             :  */
   13412           7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
   13413             :                                      size_t nCountInputArrays,
   13414             :                                      size_t *pnCountOutputArrays,
   13415             :                                      CSLConstList papszOptions)
   13416             : {
   13417           7 :     VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
   13418           7 :     VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
   13419             : 
   13420          14 :     std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
   13421          20 :     for (size_t i = 0; i < nCountInputArrays; ++i)
   13422          13 :         apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
   13423             : 
   13424             :     const auto apoOutputArrays =
   13425           7 :         GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
   13426             :     auto ret = static_cast<GDALMDArrayH *>(
   13427           7 :         CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
   13428          17 :     for (size_t i = 0; i < apoOutputArrays.size(); i++)
   13429             :     {
   13430          10 :         ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
   13431             :     }
   13432           7 :     *pnCountOutputArrays = apoOutputArrays.size();
   13433           7 :     return ret;
   13434             : }
   13435             : 
   13436             : /************************************************************************/
   13437             : /*                        GDALReleaseArrays()                           */
   13438             : /************************************************************************/
   13439             : 
   13440             : /** Free the return of GDALMDArrayGetCoordinateVariables()
   13441             :  *
   13442             :  * @param arrays return pointer of above methods
   13443             :  * @param nCount *pnCount value returned by above methods
   13444             :  */
   13445          20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
   13446             : {
   13447          46 :     for (size_t i = 0; i < nCount; i++)
   13448             :     {
   13449          26 :         delete arrays[i];
   13450             :     }
   13451          20 :     CPLFree(arrays);
   13452          20 : }
   13453             : 
   13454             : /************************************************************************/
   13455             : /*                           GDALMDArrayCache()                         */
   13456             : /************************************************************************/
   13457             : 
   13458             : /**
   13459             :  * \brief Cache the content of the array into an auxiliary filename.
   13460             :  *
   13461             :  * This is the same as the C++ method GDALMDArray::Cache().
   13462             :  *
   13463             :  * @since GDAL 3.4
   13464             :  */
   13465             : 
   13466           7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
   13467             : {
   13468           7 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   13469           7 :     return hArray->m_poImpl->Cache(papszOptions);
   13470             : }
   13471             : 
   13472             : /************************************************************************/
   13473             : /*                       GDALMDArrayRename()                           */
   13474             : /************************************************************************/
   13475             : 
   13476             : /** Rename the array.
   13477             :  *
   13478             :  * This is not implemented by all drivers.
   13479             :  *
   13480             :  * Drivers known to implement it: MEM, netCDF, Zarr.
   13481             :  *
   13482             :  * This is the same as the C++ method GDALAbstractMDArray::Rename()
   13483             :  *
   13484             :  * @return true in case of success
   13485             :  * @since GDAL 3.8
   13486             :  */
   13487          28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
   13488             : {
   13489          28 :     VALIDATE_POINTER1(hArray, __func__, false);
   13490          28 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   13491          28 :     return hArray->m_poImpl->Rename(pszNewName);
   13492             : }
   13493             : 
   13494             : /************************************************************************/
   13495             : /*                        GDALAttributeRelease()                        */
   13496             : /************************************************************************/
   13497             : 
   13498             : /** Release the GDAL in-memory object associated with a GDALAttribute.
   13499             :  *
   13500             :  * Note: when applied on a object coming from a driver, this does not
   13501             :  * destroy the object in the file, database, etc...
   13502             :  */
   13503         765 : void GDALAttributeRelease(GDALAttributeH hAttr)
   13504             : {
   13505         765 :     delete hAttr;
   13506         765 : }
   13507             : 
   13508             : /************************************************************************/
   13509             : /*                        GDALAttributeGetName()                        */
   13510             : /************************************************************************/
   13511             : 
   13512             : /** Return the name of the attribute.
   13513             :  *
   13514             :  * The returned pointer is valid until hAttr is released.
   13515             :  *
   13516             :  * This is the same as the C++ method GDALAttribute::GetName().
   13517             :  */
   13518         361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
   13519             : {
   13520         361 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13521         361 :     return hAttr->m_poImpl->GetName().c_str();
   13522             : }
   13523             : 
   13524             : /************************************************************************/
   13525             : /*                      GDALAttributeGetFullName()                      */
   13526             : /************************************************************************/
   13527             : 
   13528             : /** Return the full name of the attribute.
   13529             :  *
   13530             :  * The returned pointer is valid until hAttr is released.
   13531             :  *
   13532             :  * This is the same as the C++ method GDALAttribute::GetFullName().
   13533             :  */
   13534          49 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
   13535             : {
   13536          49 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13537          49 :     return hAttr->m_poImpl->GetFullName().c_str();
   13538             : }
   13539             : 
   13540             : /************************************************************************/
   13541             : /*                   GDALAttributeGetTotalElementsCount()               */
   13542             : /************************************************************************/
   13543             : 
   13544             : /** Return the total number of values in the attribute.
   13545             :  *
   13546             :  * This is the same as the C++ method
   13547             :  * GDALAbstractMDArray::GetTotalElementsCount()
   13548             :  */
   13549         177 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
   13550             : {
   13551         177 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13552         177 :     return hAttr->m_poImpl->GetTotalElementsCount();
   13553             : }
   13554             : 
   13555             : /************************************************************************/
   13556             : /*                    GDALAttributeGetDimensionCount()                */
   13557             : /************************************************************************/
   13558             : 
   13559             : /** Return the number of dimensions.
   13560             :  *
   13561             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
   13562             :  */
   13563          12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
   13564             : {
   13565          12 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13566          12 :     return hAttr->m_poImpl->GetDimensionCount();
   13567             : }
   13568             : 
   13569             : /************************************************************************/
   13570             : /*                       GDALAttributeGetDimensionsSize()                */
   13571             : /************************************************************************/
   13572             : 
   13573             : /** Return the dimension sizes of the attribute.
   13574             :  *
   13575             :  * The returned array must be freed with CPLFree()
   13576             :  *
   13577             :  * @param hAttr Attribute.
   13578             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13579             :  *
   13580             :  * @return an array of *pnCount values.
   13581             :  */
   13582          11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
   13583             : {
   13584          11 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13585          11 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13586          11 :     const auto &dims = hAttr->m_poImpl->GetDimensions();
   13587          11 :     auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
   13588          22 :     for (size_t i = 0; i < dims.size(); i++)
   13589             :     {
   13590          11 :         ret[i] = dims[i]->GetSize();
   13591             :     }
   13592          11 :     *pnCount = dims.size();
   13593          11 :     return ret;
   13594             : }
   13595             : 
   13596             : /************************************************************************/
   13597             : /*                       GDALAttributeGetDataType()                     */
   13598             : /************************************************************************/
   13599             : 
   13600             : /** Return the data type
   13601             :  *
   13602             :  * The return must be freed with GDALExtendedDataTypeRelease().
   13603             :  */
   13604         434 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
   13605             : {
   13606         434 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13607             :     return new GDALExtendedDataTypeHS(
   13608         434 :         new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
   13609             : }
   13610             : 
   13611             : /************************************************************************/
   13612             : /*                       GDALAttributeReadAsRaw()                       */
   13613             : /************************************************************************/
   13614             : 
   13615             : /** Return the raw value of an attribute.
   13616             :  *
   13617             :  * This is the same as the C++ method GDALAttribute::ReadAsRaw().
   13618             :  *
   13619             :  * The returned buffer must be freed with GDALAttributeFreeRawResult()
   13620             :  *
   13621             :  * @param hAttr Attribute.
   13622             :  * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
   13623             :  *
   13624             :  * @return a buffer of *pnSize bytes.
   13625             :  */
   13626           6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
   13627             : {
   13628           6 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13629           6 :     VALIDATE_POINTER1(pnSize, __func__, nullptr);
   13630          12 :     auto res(hAttr->m_poImpl->ReadAsRaw());
   13631           6 :     *pnSize = res.size();
   13632           6 :     auto ret = res.StealData();
   13633           6 :     if (!ret)
   13634             :     {
   13635           0 :         *pnSize = 0;
   13636           0 :         return nullptr;
   13637             :     }
   13638           6 :     return ret;
   13639             : }
   13640             : 
   13641             : /************************************************************************/
   13642             : /*                       GDALAttributeFreeRawResult()                   */
   13643             : /************************************************************************/
   13644             : 
   13645             : /** Free the return of GDALAttributeAsRaw()
   13646             :  */
   13647           6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
   13648             :                                 CPL_UNUSED size_t nSize)
   13649             : {
   13650           6 :     VALIDATE_POINTER0(hAttr, __func__);
   13651           6 :     if (raw)
   13652             :     {
   13653           6 :         const auto &dt(hAttr->m_poImpl->GetDataType());
   13654           6 :         const auto nDTSize(dt.GetSize());
   13655           6 :         GByte *pabyPtr = raw;
   13656           6 :         const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
   13657           6 :         CPLAssert(nSize == nDTSize * nEltCount);
   13658          12 :         for (size_t i = 0; i < nEltCount; ++i)
   13659             :         {
   13660           6 :             dt.FreeDynamicMemory(pabyPtr);
   13661           6 :             pabyPtr += nDTSize;
   13662             :         }
   13663           6 :         CPLFree(raw);
   13664             :     }
   13665             : }
   13666             : 
   13667             : /************************************************************************/
   13668             : /*                       GDALAttributeReadAsString()                    */
   13669             : /************************************************************************/
   13670             : 
   13671             : /** Return the value of an attribute as a string.
   13672             :  *
   13673             :  * The returned string should not be freed, and its lifetime does not
   13674             :  * excess a next call to ReadAsString() on the same object, or the deletion
   13675             :  * of the object itself.
   13676             :  *
   13677             :  * This function will only return the first element if there are several.
   13678             :  *
   13679             :  * This is the same as the C++ method GDALAttribute::ReadAsString()
   13680             :  *
   13681             :  * @return a string, or nullptr.
   13682             :  */
   13683         108 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
   13684             : {
   13685         108 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13686         108 :     return hAttr->m_poImpl->ReadAsString();
   13687             : }
   13688             : 
   13689             : /************************************************************************/
   13690             : /*                      GDALAttributeReadAsInt()                        */
   13691             : /************************************************************************/
   13692             : 
   13693             : /** Return the value of an attribute as a integer.
   13694             :  *
   13695             :  * This function will only return the first element if there are several.
   13696             :  *
   13697             :  * It can fail if its value can not be converted to integer.
   13698             :  *
   13699             :  * This is the same as the C++ method GDALAttribute::ReadAsInt()
   13700             :  *
   13701             :  * @return a integer, or INT_MIN in case of error.
   13702             :  */
   13703          22 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
   13704             : {
   13705          22 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13706          22 :     return hAttr->m_poImpl->ReadAsInt();
   13707             : }
   13708             : 
   13709             : /************************************************************************/
   13710             : /*                      GDALAttributeReadAsInt64()                      */
   13711             : /************************************************************************/
   13712             : 
   13713             : /** Return the value of an attribute as a int64_t.
   13714             :  *
   13715             :  * This function will only return the first element if there are several.
   13716             :  *
   13717             :  * It can fail if its value can not be converted to integer.
   13718             :  *
   13719             :  * This is the same as the C++ method GDALAttribute::ReadAsInt64()
   13720             :  *
   13721             :  * @return an int64_t, or INT64_MIN in case of error.
   13722             :  */
   13723          15 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
   13724             : {
   13725          15 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13726          15 :     return hAttr->m_poImpl->ReadAsInt64();
   13727             : }
   13728             : 
   13729             : /************************************************************************/
   13730             : /*                       GDALAttributeReadAsDouble()                    */
   13731             : /************************************************************************/
   13732             : 
   13733             : /** Return the value of an attribute as a double.
   13734             :  *
   13735             :  * This function will only return the first element if there are several.
   13736             :  *
   13737             :  * It can fail if its value can not be converted to double.
   13738             :  *
   13739             :  * This is the same as the C++ method GDALAttribute::ReadAsDouble()
   13740             :  *
   13741             :  * @return a double value.
   13742             :  */
   13743          40 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
   13744             : {
   13745          40 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13746          40 :     return hAttr->m_poImpl->ReadAsDouble();
   13747             : }
   13748             : 
   13749             : /************************************************************************/
   13750             : /*                     GDALAttributeReadAsStringArray()                 */
   13751             : /************************************************************************/
   13752             : 
   13753             : /** Return the value of an attribute as an array of strings.
   13754             :  *
   13755             :  * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
   13756             :  *
   13757             :  * The return value must be freed with CSLDestroy().
   13758             :  */
   13759          19 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
   13760             : {
   13761          19 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13762          19 :     return hAttr->m_poImpl->ReadAsStringArray().StealList();
   13763             : }
   13764             : 
   13765             : /************************************************************************/
   13766             : /*                     GDALAttributeReadAsIntArray()                    */
   13767             : /************************************************************************/
   13768             : 
   13769             : /** Return the value of an attribute as an array of integers.
   13770             :  *
   13771             :  * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
   13772             :  *
   13773             :  * @param hAttr Attribute
   13774             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13775             :  * @return array to be freed with CPLFree(), or nullptr.
   13776             :  */
   13777          15 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
   13778             : {
   13779          15 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13780          15 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13781          15 :     *pnCount = 0;
   13782          30 :     auto tmp(hAttr->m_poImpl->ReadAsIntArray());
   13783          15 :     if (tmp.empty())
   13784           0 :         return nullptr;
   13785          15 :     auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
   13786          15 :     if (!ret)
   13787           0 :         return nullptr;
   13788          15 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
   13789          15 :     *pnCount = tmp.size();
   13790          15 :     return ret;
   13791             : }
   13792             : 
   13793             : /************************************************************************/
   13794             : /*                     GDALAttributeReadAsInt64Array()                  */
   13795             : /************************************************************************/
   13796             : 
   13797             : /** Return the value of an attribute as an array of int64_t.
   13798             :  *
   13799             :  * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
   13800             :  *
   13801             :  * @param hAttr Attribute
   13802             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13803             :  * @return array to be freed with CPLFree(), or nullptr.
   13804             :  */
   13805          14 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
   13806             : {
   13807          14 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13808          14 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13809          14 :     *pnCount = 0;
   13810          28 :     auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
   13811          14 :     if (tmp.empty())
   13812           0 :         return nullptr;
   13813             :     auto ret = static_cast<int64_t *>(
   13814          14 :         VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
   13815          14 :     if (!ret)
   13816           0 :         return nullptr;
   13817          14 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
   13818          14 :     *pnCount = tmp.size();
   13819          14 :     return ret;
   13820             : }
   13821             : 
   13822             : /************************************************************************/
   13823             : /*                     GDALAttributeReadAsDoubleArray()                 */
   13824             : /************************************************************************/
   13825             : 
   13826             : /** Return the value of an attribute as an array of doubles.
   13827             :  *
   13828             :  * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
   13829             :  *
   13830             :  * @param hAttr Attribute
   13831             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13832             :  * @return array to be freed with CPLFree(), or nullptr.
   13833             :  */
   13834          29 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
   13835             : {
   13836          29 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13837          29 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13838          29 :     *pnCount = 0;
   13839          58 :     auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
   13840          29 :     if (tmp.empty())
   13841           0 :         return nullptr;
   13842             :     auto ret =
   13843          29 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
   13844          29 :     if (!ret)
   13845           0 :         return nullptr;
   13846          29 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
   13847          29 :     *pnCount = tmp.size();
   13848          29 :     return ret;
   13849             : }
   13850             : 
   13851             : /************************************************************************/
   13852             : /*                     GDALAttributeWriteRaw()                          */
   13853             : /************************************************************************/
   13854             : 
   13855             : /** Write an attribute from raw values expressed in GetDataType()
   13856             :  *
   13857             :  * The values should be provided in the type of GetDataType() and there should
   13858             :  * be exactly GetTotalElementsCount() of them.
   13859             :  * If GetDataType() is a string, each value should be a char* pointer.
   13860             :  *
   13861             :  * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
   13862             :  *
   13863             :  * @param hAttr Attribute
   13864             :  * @param pabyValue Buffer of nLen bytes.
   13865             :  * @param nLength Size of pabyValue in bytes. Should be equal to
   13866             :  *             GetTotalElementsCount() * GetDataType().GetSize()
   13867             :  * @return TRUE in case of success.
   13868             :  */
   13869           5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
   13870             :                           size_t nLength)
   13871             : {
   13872           5 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13873           5 :     return hAttr->m_poImpl->Write(pabyValue, nLength);
   13874             : }
   13875             : 
   13876             : /************************************************************************/
   13877             : /*                     GDALAttributeWriteString()                       */
   13878             : /************************************************************************/
   13879             : 
   13880             : /** Write an attribute from a string value.
   13881             :  *
   13882             :  * Type conversion will be performed if needed. If the attribute contains
   13883             :  * multiple values, only the first one will be updated.
   13884             :  *
   13885             :  * This is the same as the C++ method GDALAttribute::Write(const char*)
   13886             :  *
   13887             :  * @param hAttr Attribute
   13888             :  * @param pszVal Pointer to a string.
   13889             :  * @return TRUE in case of success.
   13890             :  */
   13891         207 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
   13892             : {
   13893         207 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13894         207 :     return hAttr->m_poImpl->Write(pszVal);
   13895             : }
   13896             : 
   13897             : /************************************************************************/
   13898             : /*                        GDALAttributeWriteInt()                       */
   13899             : /************************************************************************/
   13900             : 
   13901             : /** Write an attribute from a integer value.
   13902             :  *
   13903             :  * Type conversion will be performed if needed. If the attribute contains
   13904             :  * multiple values, only the first one will be updated.
   13905             :  *
   13906             :  * This is the same as the C++ method GDALAttribute::WriteInt()
   13907             :  *
   13908             :  * @param hAttr Attribute
   13909             :  * @param nVal Value.
   13910             :  * @return TRUE in case of success.
   13911             :  */
   13912          22 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
   13913             : {
   13914          22 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13915          22 :     return hAttr->m_poImpl->WriteInt(nVal);
   13916             : }
   13917             : 
   13918             : /************************************************************************/
   13919             : /*                        GDALAttributeWriteInt64()                     */
   13920             : /************************************************************************/
   13921             : 
   13922             : /** Write an attribute from an int64_t value.
   13923             :  *
   13924             :  * Type conversion will be performed if needed. If the attribute contains
   13925             :  * multiple values, only the first one will be updated.
   13926             :  *
   13927             :  * This is the same as the C++ method GDALAttribute::WriteLong()
   13928             :  *
   13929             :  * @param hAttr Attribute
   13930             :  * @param nVal Value.
   13931             :  * @return TRUE in case of success.
   13932             :  */
   13933          11 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
   13934             : {
   13935          11 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13936          11 :     return hAttr->m_poImpl->WriteInt64(nVal);
   13937             : }
   13938             : 
   13939             : /************************************************************************/
   13940             : /*                        GDALAttributeWriteDouble()                    */
   13941             : /************************************************************************/
   13942             : 
   13943             : /** Write an attribute from a double value.
   13944             :  *
   13945             :  * Type conversion will be performed if needed. If the attribute contains
   13946             :  * multiple values, only the first one will be updated.
   13947             :  *
   13948             :  * This is the same as the C++ method GDALAttribute::Write(double);
   13949             :  *
   13950             :  * @param hAttr Attribute
   13951             :  * @param dfVal Value.
   13952             :  *
   13953             :  * @return TRUE in case of success.
   13954             :  */
   13955          11 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
   13956             : {
   13957          11 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13958          11 :     return hAttr->m_poImpl->Write(dfVal);
   13959             : }
   13960             : 
   13961             : /************************************************************************/
   13962             : /*                       GDALAttributeWriteStringArray()                */
   13963             : /************************************************************************/
   13964             : 
   13965             : /** Write an attribute from an array of strings.
   13966             :  *
   13967             :  * Type conversion will be performed if needed.
   13968             :  *
   13969             :  * Exactly GetTotalElementsCount() strings must be provided
   13970             :  *
   13971             :  * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
   13972             :  *
   13973             :  * @param hAttr Attribute
   13974             :  * @param papszValues Array of strings.
   13975             :  * @return TRUE in case of success.
   13976             :  */
   13977           8 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
   13978             :                                   CSLConstList papszValues)
   13979             : {
   13980           8 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13981           8 :     return hAttr->m_poImpl->Write(papszValues);
   13982             : }
   13983             : 
   13984             : /************************************************************************/
   13985             : /*                       GDALAttributeWriteIntArray()                */
   13986             : /************************************************************************/
   13987             : 
   13988             : /** Write an attribute from an array of int.
   13989             :  *
   13990             :  * Type conversion will be performed if needed.
   13991             :  *
   13992             :  * Exactly GetTotalElementsCount() strings must be provided
   13993             :  *
   13994             :  * This is the same as the C++ method GDALAttribute::Write(const int *,
   13995             :  * size_t)
   13996             :  *
   13997             :  * @param hAttr Attribute
   13998             :  * @param panValues Array of int.
   13999             :  * @param nCount Should be equal to GetTotalElementsCount().
   14000             :  * @return TRUE in case of success.
   14001             :  */
   14002          11 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
   14003             :                                size_t nCount)
   14004             : {
   14005          11 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   14006          11 :     return hAttr->m_poImpl->Write(panValues, nCount);
   14007             : }
   14008             : 
   14009             : /************************************************************************/
   14010             : /*                       GDALAttributeWriteInt64Array()                 */
   14011             : /************************************************************************/
   14012             : 
   14013             : /** Write an attribute from an array of int64_t.
   14014             :  *
   14015             :  * Type conversion will be performed if needed.
   14016             :  *
   14017             :  * Exactly GetTotalElementsCount() strings must be provided
   14018             :  *
   14019             :  * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
   14020             :  * size_t)
   14021             :  *
   14022             :  * @param hAttr Attribute
   14023             :  * @param panValues Array of int64_t.
   14024             :  * @param nCount Should be equal to GetTotalElementsCount().
   14025             :  * @return TRUE in case of success.
   14026             :  */
   14027          10 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
   14028             :                                  size_t nCount)
   14029             : {
   14030          10 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   14031          10 :     return hAttr->m_poImpl->Write(panValues, nCount);
   14032             : }
   14033             : 
   14034             : /************************************************************************/
   14035             : /*                       GDALAttributeWriteDoubleArray()                */
   14036             : /************************************************************************/
   14037             : 
   14038             : /** Write an attribute from an array of double.
   14039             :  *
   14040             :  * Type conversion will be performed if needed.
   14041             :  *
   14042             :  * Exactly GetTotalElementsCount() strings must be provided
   14043             :  *
   14044             :  * This is the same as the C++ method GDALAttribute::Write(const double *,
   14045             :  * size_t)
   14046             :  *
   14047             :  * @param hAttr Attribute
   14048             :  * @param padfValues Array of double.
   14049             :  * @param nCount Should be equal to GetTotalElementsCount().
   14050             :  * @return TRUE in case of success.
   14051             :  */
   14052           7 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
   14053             :                                   const double *padfValues, size_t nCount)
   14054             : {
   14055           7 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   14056           7 :     return hAttr->m_poImpl->Write(padfValues, nCount);
   14057             : }
   14058             : 
   14059             : /************************************************************************/
   14060             : /*                      GDALAttributeRename()                           */
   14061             : /************************************************************************/
   14062             : 
   14063             : /** Rename the attribute.
   14064             :  *
   14065             :  * This is not implemented by all drivers.
   14066             :  *
   14067             :  * Drivers known to implement it: MEM, netCDF.
   14068             :  *
   14069             :  * This is the same as the C++ method GDALAbstractMDArray::Rename()
   14070             :  *
   14071             :  * @return true in case of success
   14072             :  * @since GDAL 3.8
   14073             :  */
   14074          27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
   14075             : {
   14076          27 :     VALIDATE_POINTER1(hAttr, __func__, false);
   14077          27 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   14078          27 :     return hAttr->m_poImpl->Rename(pszNewName);
   14079             : }
   14080             : 
   14081             : /************************************************************************/
   14082             : /*                        GDALDimensionRelease()                        */
   14083             : /************************************************************************/
   14084             : 
   14085             : /** Release the GDAL in-memory object associated with a GDALDimension.
   14086             :  *
   14087             :  * Note: when applied on a object coming from a driver, this does not
   14088             :  * destroy the object in the file, database, etc...
   14089             :  */
   14090        5318 : void GDALDimensionRelease(GDALDimensionH hDim)
   14091             : {
   14092        5318 :     delete hDim;
   14093        5318 : }
   14094             : 
   14095             : /************************************************************************/
   14096             : /*                        GDALDimensionGetName()                        */
   14097             : /************************************************************************/
   14098             : 
   14099             : /** Return dimension name.
   14100             :  *
   14101             :  * This is the same as the C++ method GDALDimension::GetName()
   14102             :  */
   14103         296 : const char *GDALDimensionGetName(GDALDimensionH hDim)
   14104             : {
   14105         296 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14106         296 :     return hDim->m_poImpl->GetName().c_str();
   14107             : }
   14108             : 
   14109             : /************************************************************************/
   14110             : /*                      GDALDimensionGetFullName()                      */
   14111             : /************************************************************************/
   14112             : 
   14113             : /** Return dimension full name.
   14114             :  *
   14115             :  * This is the same as the C++ method GDALDimension::GetFullName()
   14116             :  */
   14117          82 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
   14118             : {
   14119          82 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14120          82 :     return hDim->m_poImpl->GetFullName().c_str();
   14121             : }
   14122             : 
   14123             : /************************************************************************/
   14124             : /*                        GDALDimensionGetType()                        */
   14125             : /************************************************************************/
   14126             : 
   14127             : /** Return dimension type.
   14128             :  *
   14129             :  * This is the same as the C++ method GDALDimension::GetType()
   14130             :  */
   14131          70 : const char *GDALDimensionGetType(GDALDimensionH hDim)
   14132             : {
   14133          70 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14134          70 :     return hDim->m_poImpl->GetType().c_str();
   14135             : }
   14136             : 
   14137             : /************************************************************************/
   14138             : /*                     GDALDimensionGetDirection()                      */
   14139             : /************************************************************************/
   14140             : 
   14141             : /** Return dimension direction.
   14142             :  *
   14143             :  * This is the same as the C++ method GDALDimension::GetDirection()
   14144             :  */
   14145          38 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
   14146             : {
   14147          38 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14148          38 :     return hDim->m_poImpl->GetDirection().c_str();
   14149             : }
   14150             : 
   14151             : /************************************************************************/
   14152             : /*                        GDALDimensionGetSize()                        */
   14153             : /************************************************************************/
   14154             : 
   14155             : /** Return the size, that is the number of values along the dimension.
   14156             :  *
   14157             :  * This is the same as the C++ method GDALDimension::GetSize()
   14158             :  */
   14159        3958 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
   14160             : {
   14161        3958 :     VALIDATE_POINTER1(hDim, __func__, 0);
   14162        3958 :     return hDim->m_poImpl->GetSize();
   14163             : }
   14164             : 
   14165             : /************************************************************************/
   14166             : /*                     GDALDimensionGetIndexingVariable()               */
   14167             : /************************************************************************/
   14168             : 
   14169             : /** Return the variable that is used to index the dimension (if there is one).
   14170             :  *
   14171             :  * This is the array, typically one-dimensional, describing the values taken
   14172             :  * by the dimension.
   14173             :  *
   14174             :  * The returned value should be freed with GDALMDArrayRelease().
   14175             :  *
   14176             :  * This is the same as the C++ method GDALDimension::GetIndexingVariable()
   14177             :  */
   14178         140 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
   14179             : {
   14180         140 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   14181         280 :     auto var(hDim->m_poImpl->GetIndexingVariable());
   14182         140 :     if (!var)
   14183          11 :         return nullptr;
   14184         129 :     return new GDALMDArrayHS(var);
   14185             : }
   14186             : 
   14187             : /************************************************************************/
   14188             : /*                      GDALDimensionSetIndexingVariable()              */
   14189             : /************************************************************************/
   14190             : 
   14191             : /** Set the variable that is used to index the dimension.
   14192             :  *
   14193             :  * This is the array, typically one-dimensional, describing the values taken
   14194             :  * by the dimension.
   14195             :  *
   14196             :  * This is the same as the C++ method GDALDimension::SetIndexingVariable()
   14197             :  *
   14198             :  * @return TRUE in case of success.
   14199             :  */
   14200          23 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
   14201             : {
   14202          23 :     VALIDATE_POINTER1(hDim, __func__, FALSE);
   14203          69 :     return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
   14204          46 :                                                       : nullptr);
   14205             : }
   14206             : 
   14207             : /************************************************************************/
   14208             : /*                      GDALDimensionRename()                           */
   14209             : /************************************************************************/
   14210             : 
   14211             : /** Rename the dimension.
   14212             :  *
   14213             :  * This is not implemented by all drivers.
   14214             :  *
   14215             :  * Drivers known to implement it: MEM, netCDF.
   14216             :  *
   14217             :  * This is the same as the C++ method GDALDimension::Rename()
   14218             :  *
   14219             :  * @return true in case of success
   14220             :  * @since GDAL 3.8
   14221             :  */
   14222          31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
   14223             : {
   14224          31 :     VALIDATE_POINTER1(hDim, __func__, false);
   14225          31 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   14226          31 :     return hDim->m_poImpl->Rename(pszNewName);
   14227             : }
   14228             : 
   14229             : /************************************************************************/
   14230             : /*                       GDALDatasetGetRootGroup()                      */
   14231             : /************************************************************************/
   14232             : 
   14233             : /** Return the root GDALGroup of this dataset.
   14234             :  *
   14235             :  * Only valid for multidimensional datasets.
   14236             :  *
   14237             :  * The returned value must be freed with GDALGroupRelease().
   14238             :  *
   14239             :  * This is the same as the C++ method GDALDataset::GetRootGroup().
   14240             :  *
   14241             :  * @since GDAL 3.1
   14242             :  */
   14243        1245 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
   14244             : {
   14245        1245 :     VALIDATE_POINTER1(hDS, __func__, nullptr);
   14246        1245 :     auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
   14247        1245 :     return poGroup ? new GDALGroupHS(poGroup) : nullptr;
   14248             : }
   14249             : 
   14250             : /************************************************************************/
   14251             : /*                      GDALRasterBandAsMDArray()                        */
   14252             : /************************************************************************/
   14253             : 
   14254             : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
   14255             :  *
   14256             :  * The band must be linked to a GDALDataset. If this dataset is not already
   14257             :  * marked as shared, it will be, so that the returned array holds a reference
   14258             :  * to it.
   14259             :  *
   14260             :  * If the dataset has a geotransform attached, the X and Y dimensions of the
   14261             :  * returned array will have an associated indexing variable.
   14262             :  *
   14263             :  * The returned pointer must be released with GDALMDArrayRelease().
   14264             :  *
   14265             :  * This is the same as the C++ method GDALRasterBand::AsMDArray().
   14266             :  *
   14267             :  * @return a new array, or NULL.
   14268             :  *
   14269             :  * @since GDAL 3.1
   14270             :  */
   14271          24 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
   14272             : {
   14273          24 :     VALIDATE_POINTER1(hBand, __func__, nullptr);
   14274          48 :     auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
   14275          24 :     if (!poArray)
   14276           0 :         return nullptr;
   14277          24 :     return new GDALMDArrayHS(poArray);
   14278             : }
   14279             : 
   14280             : /************************************************************************/
   14281             : /*                        GDALDatasetAsMDArray()                        */
   14282             : /************************************************************************/
   14283             : 
   14284             : /** Return a view of this dataset as a 3D multidimensional GDALMDArray.
   14285             :  *
   14286             :  * If this dataset is not already marked as shared, it will be, so that the
   14287             :  * returned array holds a reference to it.
   14288             :  *
   14289             :  * If the dataset has a geotransform attached, the X and Y dimensions of the
   14290             :  * returned array will have an associated indexing variable.
   14291             :  *
   14292             :  * The currently supported list of options is:
   14293             :  * <ul>
   14294             :  * <li>DIM_ORDER=&lt;order&gt; where order can be "AUTO", "Band,Y,X" or "Y,X,Band".
   14295             :  * "Band,Y,X" means that the first (slowest changing) dimension is Band
   14296             :  * and the last (fastest changing direction) is X
   14297             :  * "Y,X,Band" means that the first (slowest changing) dimension is Y
   14298             :  * and the last (fastest changing direction) is Band.
   14299             :  * "AUTO" (the default) selects "Band,Y,X" for single band datasets, or takes
   14300             :  * into account the INTERLEAVE metadata item in the IMAGE_STRUCTURE domain.
   14301             :  * If it equals BAND, then "Band,Y,X" is used. Otherwise (if it equals PIXEL),
   14302             :  * "Y,X,Band" is use.
   14303             :  * </li>
   14304             :  * <li>BAND_INDEXING_VAR_ITEM={Description}|{None}|{Index}|{ColorInterpretation}|&lt;BandMetadataItem&gt;:
   14305             :  * item from which to build the band indexing variable.
   14306             :  * <ul>
   14307             :  * <li>"{Description}", the default, means to use the band description (or "Band index" if empty).</li>
   14308             :  * <li>"{None}" means that no band indexing variable must be created.</li>
   14309             :  * <li>"{Index}" means that the band index (starting at one) is used.</li>
   14310             :  * <li>"{ColorInterpretation}" means that the band color interpretation is used (i.e. "Red", "Green", "Blue").</li>
   14311             :  * <li>&lt;BandMetadataItem&gt; is the name of a band metadata item to use.</li>
   14312             :  * </ul>
   14313             :  * </li>
   14314             :  * <li>BAND_INDEXING_VAR_TYPE=String|Real|Integer: the data type of the band
   14315             :  * indexing variable, when BAND_INDEXING_VAR_ITEM corresponds to a band metadata item.
   14316             :  * Defaults to String.
   14317             :  * </li>
   14318             :  * <li>BAND_DIM_NAME=&lt;string&gt;: Name of the band dimension.
   14319             :  * Defaults to "Band".
   14320             :  * </li>
   14321             :  * <li>X_DIM_NAME=&lt;string&gt;: Name of the X dimension. Defaults to "X".
   14322             :  * </li>
   14323             :  * <li>Y_DIM_NAME=&lt;string&gt;: Name of the Y dimension. Defaults to "Y".
   14324             :  * </li>
   14325             :  * </ul>
   14326             :  *
   14327             :  * The returned pointer must be released with GDALMDArrayRelease().
   14328             :  *
   14329             :  * The "reverse" methods are GDALRasterBand::AsMDArray() and
   14330             :  * GDALDataset::AsMDArray()
   14331             :  *
   14332             :  * This is the same as the C++ method GDALDataset::AsMDArray().
   14333             :  *
   14334             :  * @param hDS Dataset handle.
   14335             :  * @param papszOptions Null-terminated list of strings, or nullptr.
   14336             :  * @return a new array, or NULL.
   14337             :  *
   14338             :  * @since GDAL 3.12
   14339             :  */
   14340          14 : GDALMDArrayH GDALDatasetAsMDArray(GDALDatasetH hDS, CSLConstList papszOptions)
   14341             : {
   14342          14 :     VALIDATE_POINTER1(hDS, __func__, nullptr);
   14343          28 :     auto poArray(GDALDataset::FromHandle(hDS)->AsMDArray(papszOptions));
   14344          14 :     if (!poArray)
   14345           3 :         return nullptr;
   14346          11 :     return new GDALMDArrayHS(poArray);
   14347             : }
   14348             : 
   14349             : /************************************************************************/
   14350             : /*                       GDALMDArrayAsClassicDataset()                  */
   14351             : /************************************************************************/
   14352             : 
   14353             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   14354             :  *
   14355             :  * Only 2D or more arrays are supported.
   14356             :  *
   14357             :  * In the case of > 2D arrays, additional dimensions will be represented as
   14358             :  * raster bands.
   14359             :  *
   14360             :  * The "reverse" methods are GDALRasterBand::AsMDArray() and
   14361             :  * GDALDataset::AsMDArray()
   14362             :  *
   14363             :  * This is the same as the C++ method GDALMDArray::AsClassicDataset().
   14364             :  *
   14365             :  * @param hArray Array.
   14366             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   14367             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   14368             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   14369             :  */
   14370           0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
   14371             :                                          size_t iYDim)
   14372             : {
   14373           0 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   14374           0 :     return GDALDataset::ToHandle(
   14375           0 :         hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
   14376             : }
   14377             : 
   14378             : /************************************************************************/
   14379             : /*                     GDALMDArrayAsClassicDatasetEx()                  */
   14380             : /************************************************************************/
   14381             : 
   14382             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   14383             :  *
   14384             :  * Only 2D or more arrays are supported.
   14385             :  *
   14386             :  * In the case of > 2D arrays, additional dimensions will be represented as
   14387             :  * raster bands.
   14388             :  *
   14389             :  * The "reverse" method is GDALRasterBand::AsMDArray().
   14390             :  *
   14391             :  * This is the same as the C++ method GDALMDArray::AsClassicDataset().
   14392             :  * @param hArray Array.
   14393             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   14394             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   14395             :  *              Ignored if the dimension count is 1.
   14396             :  * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
   14397             :  *                   BAND_IMAGERY_METADATA option.
   14398             :  * @param papszOptions Cf GDALMDArray::AsClassicDataset()
   14399             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   14400             :  * @since GDAL 3.8
   14401             :  */
   14402         100 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
   14403             :                                            size_t iYDim, GDALGroupH hRootGroup,
   14404             :                                            CSLConstList papszOptions)
   14405             : {
   14406         100 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   14407         200 :     return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
   14408         200 :         iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
   14409         200 :         papszOptions));
   14410             : }
   14411             : 
   14412             : //! @cond Doxygen_Suppress
   14413             : 
   14414         180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
   14415             :                                          const std::string &osName,
   14416             :                                          const std::string &osValue,
   14417         180 :                                          GDALExtendedDataTypeSubType eSubType)
   14418             :     : GDALAbstractMDArray(osParentName, osName),
   14419             :       GDALAttribute(osParentName, osName),
   14420         180 :       m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
   14421             : {
   14422         180 : }
   14423             : 
   14424             : const std::vector<std::shared_ptr<GDALDimension>> &
   14425          30 : GDALAttributeString::GetDimensions() const
   14426             : {
   14427          30 :     return m_dims;
   14428             : }
   14429             : 
   14430          21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
   14431             : {
   14432          21 :     return m_dt;
   14433             : }
   14434             : 
   14435          10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
   14436             :                                 const GPtrDiff_t *,
   14437             :                                 const GDALExtendedDataType &bufferDataType,
   14438             :                                 void *pDstBuffer) const
   14439             : {
   14440          10 :     if (bufferDataType.GetClass() != GEDTC_STRING)
   14441           0 :         return false;
   14442          10 :     char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
   14443          10 :     if (!pszStr)
   14444           0 :         return false;
   14445          10 :     memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
   14446          10 :     *static_cast<char **>(pDstBuffer) = pszStr;
   14447          10 :     return true;
   14448             : }
   14449             : 
   14450          66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   14451             :                                            const std::string &osName,
   14452          66 :                                            double dfValue)
   14453             :     : GDALAbstractMDArray(osParentName, osName),
   14454             :       GDALAttribute(osParentName, osName),
   14455          66 :       m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
   14456             : {
   14457          66 : }
   14458             : 
   14459          27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   14460             :                                            const std::string &osName,
   14461          27 :                                            int nValue)
   14462             :     : GDALAbstractMDArray(osParentName, osName),
   14463             :       GDALAttribute(osParentName, osName),
   14464          27 :       m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
   14465             : {
   14466          27 : }
   14467             : 
   14468           7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   14469             :                                            const std::string &osName,
   14470           7 :                                            const std::vector<GUInt32> &anValues)
   14471             :     : GDALAbstractMDArray(osParentName, osName),
   14472             :       GDALAttribute(osParentName, osName),
   14473           7 :       m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
   14474             : {
   14475           7 :     m_dims.push_back(std::make_shared<GDALDimension>(
   14476          14 :         std::string(), "dim0", std::string(), std::string(),
   14477           7 :         m_anValuesUInt32.size()));
   14478           7 : }
   14479             : 
   14480             : const std::vector<std::shared_ptr<GDALDimension>> &
   14481          14 : GDALAttributeNumeric::GetDimensions() const
   14482             : {
   14483          14 :     return m_dims;
   14484             : }
   14485             : 
   14486           8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
   14487             : {
   14488           8 :     return m_dt;
   14489             : }
   14490             : 
   14491           4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
   14492             :                                  const size_t *count, const GInt64 *arrayStep,
   14493             :                                  const GPtrDiff_t *bufferStride,
   14494             :                                  const GDALExtendedDataType &bufferDataType,
   14495             :                                  void *pDstBuffer) const
   14496             : {
   14497           4 :     if (m_dims.empty())
   14498             :     {
   14499           3 :         if (m_dt.GetNumericDataType() == GDT_Float64)
   14500           0 :             GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
   14501             :                                             bufferDataType);
   14502             :         else
   14503             :         {
   14504           3 :             CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
   14505           3 :             GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
   14506             :                                             bufferDataType);
   14507             :         }
   14508             :     }
   14509             :     else
   14510             :     {
   14511           1 :         CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
   14512           1 :         GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
   14513          30 :         for (size_t i = 0; i < count[0]; ++i)
   14514             :         {
   14515          29 :             GDALExtendedDataType::CopyValue(
   14516          29 :                 &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
   14517          29 :                                                       i * arrayStep[0])],
   14518          29 :                 m_dt, pabyDstBuffer, bufferDataType);
   14519          29 :             pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
   14520             :         }
   14521             :     }
   14522           4 :     return true;
   14523             : }
   14524             : 
   14525         224 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
   14526             :     const std::string &osParentName, const std::string &osName,
   14527             :     const std::shared_ptr<GDALDimension> &poDim, double dfStart,
   14528         224 :     double dfIncrement, double dfOffsetInIncrement)
   14529             :     : GDALAbstractMDArray(osParentName, osName),
   14530             :       GDALMDArray(osParentName, osName), m_dfStart(dfStart),
   14531             :       m_dfIncrement(dfIncrement),
   14532         448 :       m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
   14533             : {
   14534         224 : }
   14535             : 
   14536         224 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
   14537             :     const std::string &osParentName, const std::string &osName,
   14538             :     const std::shared_ptr<GDALDimension> &poDim, double dfStart,
   14539             :     double dfIncrement, double dfOffsetInIncrement)
   14540             : {
   14541             :     auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
   14542         224 :         osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
   14543         224 :     poArray->SetSelf(poArray);
   14544         224 :     return poArray;
   14545             : }
   14546             : 
   14547             : const std::vector<std::shared_ptr<GDALDimension>> &
   14548        1351 : GDALMDArrayRegularlySpaced::GetDimensions() const
   14549             : {
   14550        1351 :     return m_dims;
   14551             : }
   14552             : 
   14553         570 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
   14554             : {
   14555         570 :     return m_dt;
   14556             : }
   14557             : 
   14558             : std::vector<std::shared_ptr<GDALAttribute>>
   14559           4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
   14560             : {
   14561           4 :     return m_attributes;
   14562             : }
   14563             : 
   14564           0 : void GDALMDArrayRegularlySpaced::AddAttribute(
   14565             :     const std::shared_ptr<GDALAttribute> &poAttr)
   14566             : {
   14567           0 :     m_attributes.emplace_back(poAttr);
   14568           0 : }
   14569             : 
   14570         293 : bool GDALMDArrayRegularlySpaced::IRead(
   14571             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
   14572             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
   14573             :     void *pDstBuffer) const
   14574             : {
   14575         293 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
   14576       16237 :     for (size_t i = 0; i < count[0]; i++)
   14577             :     {
   14578       15944 :         const double dfVal =
   14579       15944 :             m_dfStart +
   14580       15944 :             (arrayStartIdx[0] + i * static_cast<double>(arrayStep[0]) +
   14581       15944 :              m_dfOffsetInIncrement) *
   14582       15944 :                 m_dfIncrement;
   14583       15944 :         GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
   14584             :                                         bufferDataType);
   14585       15944 :         pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
   14586             :     }
   14587         293 :     return true;
   14588             : }
   14589             : 
   14590        3978 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
   14591             :     const std::string &osParentName, const std::string &osName,
   14592        3978 :     const std::string &osType, const std::string &osDirection, GUInt64 nSize)
   14593        3978 :     : GDALDimension(osParentName, osName, osType, osDirection, nSize)
   14594             : {
   14595        3978 : }
   14596             : 
   14597             : std::shared_ptr<GDALMDArray>
   14598        1458 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
   14599             : {
   14600        1458 :     return m_poIndexingVariable.lock();
   14601             : }
   14602             : 
   14603             : // cppcheck-suppress passedByValue
   14604         644 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
   14605             :     std::shared_ptr<GDALMDArray> poIndexingVariable)
   14606             : {
   14607         644 :     m_poIndexingVariable = poIndexingVariable;
   14608         644 :     return true;
   14609             : }
   14610             : 
   14611          33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
   14612             : {
   14613          33 :     m_nSize = nNewSize;
   14614          33 : }
   14615             : 
   14616             : /************************************************************************/
   14617             : /*                       GDALPamMultiDim::Private                       */
   14618             : /************************************************************************/
   14619             : 
   14620             : struct GDALPamMultiDim::Private
   14621             : {
   14622             :     std::string m_osFilename{};
   14623             :     std::string m_osPamFilename{};
   14624             : 
   14625             :     struct Statistics
   14626             :     {
   14627             :         bool bHasStats = false;
   14628             :         bool bApproxStats = false;
   14629             :         double dfMin = 0;
   14630             :         double dfMax = 0;
   14631             :         double dfMean = 0;
   14632             :         double dfStdDev = 0;
   14633             :         GUInt64 nValidCount = 0;
   14634             :     };
   14635             : 
   14636             :     struct ArrayInfo
   14637             :     {
   14638             :         std::shared_ptr<OGRSpatialReference> poSRS{};
   14639             :         // cppcheck-suppress unusedStructMember
   14640             :         Statistics stats{};
   14641             :     };
   14642             : 
   14643             :     typedef std::pair<std::string, std::string> NameContext;
   14644             :     std::map<NameContext, ArrayInfo> m_oMapArray{};
   14645             :     std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
   14646             :     bool m_bDirty = false;
   14647             :     bool m_bLoaded = false;
   14648             : };
   14649             : 
   14650             : /************************************************************************/
   14651             : /*                          GDALPamMultiDim                             */
   14652             : /************************************************************************/
   14653             : 
   14654        1613 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
   14655        1613 :     : d(new Private())
   14656             : {
   14657        1613 :     d->m_osFilename = osFilename;
   14658        1612 : }
   14659             : 
   14660             : /************************************************************************/
   14661             : /*                   GDALPamMultiDim::~GDALPamMultiDim()                */
   14662             : /************************************************************************/
   14663             : 
   14664        1613 : GDALPamMultiDim::~GDALPamMultiDim()
   14665             : {
   14666        1613 :     if (d->m_bDirty)
   14667          30 :         Save();
   14668        1613 : }
   14669             : 
   14670             : /************************************************************************/
   14671             : /*                          GDALPamMultiDim::Load()                     */
   14672             : /************************************************************************/
   14673             : 
   14674         107 : void GDALPamMultiDim::Load()
   14675             : {
   14676         107 :     if (d->m_bLoaded)
   14677          96 :         return;
   14678          45 :     d->m_bLoaded = true;
   14679             : 
   14680          45 :     const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
   14681          45 :     d->m_osPamFilename =
   14682          90 :         pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
   14683          45 :     CPLXMLTreeCloser oTree(nullptr);
   14684             :     {
   14685          90 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
   14686          45 :         oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
   14687             :     }
   14688          45 :     if (!oTree)
   14689             :     {
   14690          34 :         return;
   14691             :     }
   14692          11 :     const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
   14693          11 :     if (!poPAMMultiDim)
   14694           0 :         return;
   14695          35 :     for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
   14696          24 :          psIter = psIter->psNext)
   14697             :     {
   14698          24 :         if (psIter->eType == CXT_Element &&
   14699          24 :             strcmp(psIter->pszValue, "Array") == 0)
   14700             :         {
   14701          13 :             const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
   14702          13 :             if (!pszName)
   14703           0 :                 continue;
   14704          13 :             const char *pszContext = CPLGetXMLValue(psIter, "context", "");
   14705             :             const auto oKey =
   14706          26 :                 std::pair<std::string, std::string>(pszName, pszContext);
   14707             : 
   14708             :             /* --------------------------------------------------------------------
   14709             :              */
   14710             :             /*      Check for an SRS node. */
   14711             :             /* --------------------------------------------------------------------
   14712             :              */
   14713          13 :             const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
   14714          13 :             if (psSRSNode)
   14715             :             {
   14716             :                 std::shared_ptr<OGRSpatialReference> poSRS =
   14717           6 :                     std::make_shared<OGRSpatialReference>();
   14718           3 :                 poSRS->SetFromUserInput(
   14719             :                     CPLGetXMLValue(psSRSNode, nullptr, ""),
   14720             :                     OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
   14721           3 :                 const char *pszMapping = CPLGetXMLValue(
   14722             :                     psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
   14723           3 :                 if (pszMapping)
   14724             :                 {
   14725             :                     char **papszTokens =
   14726           3 :                         CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
   14727           6 :                     std::vector<int> anMapping;
   14728           9 :                     for (int i = 0; papszTokens && papszTokens[i]; i++)
   14729             :                     {
   14730           6 :                         anMapping.push_back(atoi(papszTokens[i]));
   14731             :                     }
   14732           3 :                     CSLDestroy(papszTokens);
   14733           3 :                     poSRS->SetDataAxisToSRSAxisMapping(anMapping);
   14734             :                 }
   14735             :                 else
   14736             :                 {
   14737           0 :                     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
   14738             :                 }
   14739             : 
   14740             :                 const char *pszCoordinateEpoch =
   14741           3 :                     CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
   14742           3 :                 if (pszCoordinateEpoch)
   14743           3 :                     poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
   14744             : 
   14745           3 :                 d->m_oMapArray[oKey].poSRS = std::move(poSRS);
   14746             :             }
   14747             : 
   14748             :             const CPLXMLNode *psStatistics =
   14749          13 :                 CPLGetXMLNode(psIter, "Statistics");
   14750          13 :             if (psStatistics)
   14751             :             {
   14752           7 :                 Private::Statistics sStats;
   14753           7 :                 sStats.bHasStats = true;
   14754           7 :                 sStats.bApproxStats = CPLTestBool(
   14755             :                     CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
   14756           7 :                 sStats.dfMin =
   14757           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
   14758           7 :                 sStats.dfMax =
   14759           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
   14760           7 :                 sStats.dfMean =
   14761           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
   14762           7 :                 sStats.dfStdDev =
   14763           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
   14764           7 :                 sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
   14765             :                     CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
   14766           7 :                 d->m_oMapArray[oKey].stats = sStats;
   14767          13 :             }
   14768             :         }
   14769             :         else
   14770             :         {
   14771          11 :             CPLXMLNode *psNextBackup = psIter->psNext;
   14772          11 :             psIter->psNext = nullptr;
   14773          11 :             d->m_apoOtherNodes.emplace_back(
   14774          11 :                 CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
   14775          11 :             psIter->psNext = psNextBackup;
   14776             :         }
   14777             :     }
   14778             : }
   14779             : 
   14780             : /************************************************************************/
   14781             : /*                          GDALPamMultiDim::Save()                     */
   14782             : /************************************************************************/
   14783             : 
   14784          30 : void GDALPamMultiDim::Save()
   14785             : {
   14786             :     CPLXMLTreeCloser oTree(
   14787          60 :         CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
   14788          34 :     for (const auto &poOtherNode : d->m_apoOtherNodes)
   14789             :     {
   14790           4 :         CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
   14791             :     }
   14792         112 :     for (const auto &kv : d->m_oMapArray)
   14793             :     {
   14794             :         CPLXMLNode *psArrayNode =
   14795          82 :             CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
   14796          82 :         CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
   14797          82 :         if (!kv.first.second.empty())
   14798             :         {
   14799           1 :             CPLAddXMLAttributeAndValue(psArrayNode, "context",
   14800             :                                        kv.first.second.c_str());
   14801             :         }
   14802          82 :         if (kv.second.poSRS)
   14803             :         {
   14804          71 :             char *pszWKT = nullptr;
   14805             :             {
   14806         142 :                 CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
   14807          71 :                 const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
   14808          71 :                 kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
   14809             :             }
   14810             :             CPLXMLNode *psSRSNode =
   14811          71 :                 CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
   14812          71 :             CPLFree(pszWKT);
   14813             :             const auto &mapping =
   14814          71 :                 kv.second.poSRS->GetDataAxisToSRSAxisMapping();
   14815         142 :             CPLString osMapping;
   14816         213 :             for (size_t i = 0; i < mapping.size(); ++i)
   14817             :             {
   14818         142 :                 if (!osMapping.empty())
   14819          71 :                     osMapping += ",";
   14820         142 :                 osMapping += CPLSPrintf("%d", mapping[i]);
   14821             :             }
   14822          71 :             CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
   14823             :                                        osMapping.c_str());
   14824             : 
   14825             :             const double dfCoordinateEpoch =
   14826          71 :                 kv.second.poSRS->GetCoordinateEpoch();
   14827          71 :             if (dfCoordinateEpoch > 0)
   14828             :             {
   14829             :                 std::string osCoordinateEpoch =
   14830           2 :                     CPLSPrintf("%f", dfCoordinateEpoch);
   14831           1 :                 if (osCoordinateEpoch.find('.') != std::string::npos)
   14832             :                 {
   14833           6 :                     while (osCoordinateEpoch.back() == '0')
   14834           5 :                         osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
   14835             :                 }
   14836           1 :                 CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
   14837             :                                            osCoordinateEpoch.c_str());
   14838             :             }
   14839             :         }
   14840             : 
   14841          82 :         if (kv.second.stats.bHasStats)
   14842             :         {
   14843             :             CPLXMLNode *psMDArray =
   14844           8 :                 CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
   14845           8 :             CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
   14846           8 :                                         kv.second.stats.bApproxStats ? "1"
   14847             :                                                                      : "0");
   14848           8 :             CPLCreateXMLElementAndValue(
   14849             :                 psMDArray, "Minimum",
   14850           8 :                 CPLSPrintf("%.17g", kv.second.stats.dfMin));
   14851           8 :             CPLCreateXMLElementAndValue(
   14852             :                 psMDArray, "Maximum",
   14853           8 :                 CPLSPrintf("%.17g", kv.second.stats.dfMax));
   14854           8 :             CPLCreateXMLElementAndValue(
   14855           8 :                 psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
   14856           8 :             CPLCreateXMLElementAndValue(
   14857             :                 psMDArray, "StdDev",
   14858           8 :                 CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
   14859           8 :             CPLCreateXMLElementAndValue(
   14860             :                 psMDArray, "ValidSampleCount",
   14861           8 :                 CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
   14862             :         }
   14863             :     }
   14864             : 
   14865             :     int bSaved;
   14866          60 :     CPLErrorAccumulator oErrorAccumulator;
   14867             :     {
   14868          30 :         auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
   14869          30 :         CPL_IGNORE_RET_VAL(oAccumulator);
   14870             :         bSaved =
   14871          30 :             CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
   14872             :     }
   14873             : 
   14874          30 :     const char *pszNewPam = nullptr;
   14875          30 :     if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
   14876           0 :         ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
   14877             :     {
   14878           0 :         CPLErrorReset();
   14879           0 :         CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
   14880             :     }
   14881             :     else
   14882             :     {
   14883          30 :         oErrorAccumulator.ReplayErrors();
   14884             :     }
   14885          30 : }
   14886             : 
   14887             : /************************************************************************/
   14888             : /*                    GDALPamMultiDim::GetSpatialRef()                  */
   14889             : /************************************************************************/
   14890             : 
   14891             : std::shared_ptr<OGRSpatialReference>
   14892          10 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
   14893             :                                const std::string &osContext)
   14894             : {
   14895          10 :     Load();
   14896             :     auto oIter =
   14897          10 :         d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
   14898          10 :     if (oIter != d->m_oMapArray.end())
   14899           2 :         return oIter->second.poSRS;
   14900           8 :     return nullptr;
   14901             : }
   14902             : 
   14903             : /************************************************************************/
   14904             : /*                    GDALPamMultiDim::SetSpatialRef()                  */
   14905             : /************************************************************************/
   14906             : 
   14907          72 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
   14908             :                                     const std::string &osContext,
   14909             :                                     const OGRSpatialReference *poSRS)
   14910             : {
   14911          72 :     Load();
   14912          72 :     d->m_bDirty = true;
   14913          72 :     if (poSRS && !poSRS->IsEmpty())
   14914          71 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
   14915             :             poSRS->Clone());
   14916             :     else
   14917           2 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
   14918           1 :             .poSRS.reset();
   14919          72 : }
   14920             : 
   14921             : /************************************************************************/
   14922             : /*                           GetStatistics()                            */
   14923             : /************************************************************************/
   14924             : 
   14925          16 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
   14926             :                                       const std::string &osContext,
   14927             :                                       bool bApproxOK, double *pdfMin,
   14928             :                                       double *pdfMax, double *pdfMean,
   14929             :                                       double *pdfStdDev, GUInt64 *pnValidCount)
   14930             : {
   14931          16 :     Load();
   14932             :     auto oIter =
   14933          16 :         d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
   14934          16 :     if (oIter == d->m_oMapArray.end())
   14935           9 :         return CE_Failure;
   14936           7 :     const auto &stats = oIter->second.stats;
   14937           7 :     if (!stats.bHasStats)
   14938           1 :         return CE_Failure;
   14939           6 :     if (!bApproxOK && stats.bApproxStats)
   14940           0 :         return CE_Failure;
   14941           6 :     if (pdfMin)
   14942           6 :         *pdfMin = stats.dfMin;
   14943           6 :     if (pdfMax)
   14944           6 :         *pdfMax = stats.dfMax;
   14945           6 :     if (pdfMean)
   14946           6 :         *pdfMean = stats.dfMean;
   14947           6 :     if (pdfStdDev)
   14948           6 :         *pdfStdDev = stats.dfStdDev;
   14949           6 :     if (pnValidCount)
   14950           6 :         *pnValidCount = stats.nValidCount;
   14951           6 :     return CE_None;
   14952             : }
   14953             : 
   14954             : /************************************************************************/
   14955             : /*                           SetStatistics()                            */
   14956             : /************************************************************************/
   14957             : 
   14958           8 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
   14959             :                                     const std::string &osContext,
   14960             :                                     bool bApproxStats, double dfMin,
   14961             :                                     double dfMax, double dfMean,
   14962             :                                     double dfStdDev, GUInt64 nValidCount)
   14963             : {
   14964           8 :     Load();
   14965           8 :     d->m_bDirty = true;
   14966             :     auto &stats =
   14967           8 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
   14968           8 :     stats.bHasStats = true;
   14969           8 :     stats.bApproxStats = bApproxStats;
   14970           8 :     stats.dfMin = dfMin;
   14971           8 :     stats.dfMax = dfMax;
   14972           8 :     stats.dfMean = dfMean;
   14973           8 :     stats.dfStdDev = dfStdDev;
   14974           8 :     stats.nValidCount = nValidCount;
   14975           8 : }
   14976             : 
   14977             : /************************************************************************/
   14978             : /*                           ClearStatistics()                          */
   14979             : /************************************************************************/
   14980             : 
   14981           0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
   14982             :                                       const std::string &osContext)
   14983             : {
   14984           0 :     Load();
   14985           0 :     d->m_bDirty = true;
   14986           0 :     d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
   14987             :         false;
   14988           0 : }
   14989             : 
   14990             : /************************************************************************/
   14991             : /*                           ClearStatistics()                          */
   14992             : /************************************************************************/
   14993             : 
   14994           1 : void GDALPamMultiDim::ClearStatistics()
   14995             : {
   14996           1 :     Load();
   14997           1 :     d->m_bDirty = true;
   14998           3 :     for (auto &kv : d->m_oMapArray)
   14999           2 :         kv.second.stats.bHasStats = false;
   15000           1 : }
   15001             : 
   15002             : /************************************************************************/
   15003             : /*                             GetPAM()                                 */
   15004             : /************************************************************************/
   15005             : 
   15006             : /*static*/ std::shared_ptr<GDALPamMultiDim>
   15007         935 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
   15008             : {
   15009         935 :     auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
   15010         935 :     if (poPamArray)
   15011         581 :         return poPamArray->GetPAM();
   15012         354 :     return nullptr;
   15013             : }
   15014             : 
   15015             : /************************************************************************/
   15016             : /*                           GDALPamMDArray                             */
   15017             : /************************************************************************/
   15018             : 
   15019        4318 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
   15020             :                                const std::string &osName,
   15021             :                                const std::shared_ptr<GDALPamMultiDim> &poPam,
   15022           0 :                                const std::string &osContext)
   15023             :     :
   15024             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
   15025             :       GDALAbstractMDArray(osParentName, osName),
   15026             : #endif
   15027        4318 :       GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
   15028             : {
   15029        4318 : }
   15030             : 
   15031             : /************************************************************************/
   15032             : /*                    GDALPamMDArray::SetSpatialRef()                   */
   15033             : /************************************************************************/
   15034             : 
   15035          72 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
   15036             : {
   15037          72 :     if (!m_poPam)
   15038           0 :         return false;
   15039          72 :     m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
   15040          72 :     return true;
   15041             : }
   15042             : 
   15043             : /************************************************************************/
   15044             : /*                    GDALPamMDArray::GetSpatialRef()                   */
   15045             : /************************************************************************/
   15046             : 
   15047          10 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
   15048             : {
   15049          10 :     if (!m_poPam)
   15050           0 :         return nullptr;
   15051          10 :     return m_poPam->GetSpatialRef(GetFullName(), GetContext());
   15052             : }
   15053             : 
   15054             : /************************************************************************/
   15055             : /*                           GetStatistics()                            */
   15056             : /************************************************************************/
   15057             : 
   15058          16 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
   15059             :                                      double *pdfMin, double *pdfMax,
   15060             :                                      double *pdfMean, double *pdfStdDev,
   15061             :                                      GUInt64 *pnValidCount,
   15062             :                                      GDALProgressFunc pfnProgress,
   15063             :                                      void *pProgressData)
   15064             : {
   15065          16 :     if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
   15066             :                                           bApproxOK, pdfMin, pdfMax, pdfMean,
   15067          16 :                                           pdfStdDev, pnValidCount) == CE_None)
   15068             :     {
   15069           6 :         return CE_None;
   15070             :     }
   15071          10 :     if (!bForce)
   15072           4 :         return CE_Warning;
   15073             : 
   15074           6 :     return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
   15075             :                                       pdfMean, pdfStdDev, pnValidCount,
   15076           6 :                                       pfnProgress, pProgressData);
   15077             : }
   15078             : 
   15079             : /************************************************************************/
   15080             : /*                           SetStatistics()                            */
   15081             : /************************************************************************/
   15082             : 
   15083           8 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
   15084             :                                    double dfMax, double dfMean, double dfStdDev,
   15085             :                                    GUInt64 nValidCount,
   15086             :                                    CSLConstList /* papszOptions */)
   15087             : {
   15088           8 :     if (!m_poPam)
   15089           0 :         return false;
   15090           8 :     m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
   15091             :                            dfMax, dfMean, dfStdDev, nValidCount);
   15092           8 :     return true;
   15093             : }
   15094             : 
   15095             : /************************************************************************/
   15096             : /*                           ClearStatistics()                          */
   15097             : /************************************************************************/
   15098             : 
   15099           0 : void GDALPamMDArray::ClearStatistics()
   15100             : {
   15101           0 :     if (!m_poPam)
   15102           0 :         return;
   15103           0 :     m_poPam->ClearStatistics(GetFullName(), GetContext());
   15104             : }
   15105             : 
   15106             : /************************************************************************/
   15107             : /*                       GDALMDIAsAttribute::GetDimensions()            */
   15108             : /************************************************************************/
   15109             : 
   15110             : const std::vector<std::shared_ptr<GDALDimension>> &
   15111          29 : GDALMDIAsAttribute::GetDimensions() const
   15112             : {
   15113          29 :     return m_dims;
   15114             : }
   15115             : 
   15116             : //! @endcond

Generated by: LCOV version 1.14