LCOV - code coverage report
Current view: top level - gcore - gdalmultidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 4690 5191 90.3 %
Date: 2025-01-18 12:42:00 Functions: 462 532 86.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Name:     gdalmultidim.cpp
       4             :  * Project:  GDAL Core
       5             :  * Purpose:  GDAL Core C++/Private implementation for multidimensional support
       6             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include <assert.h>
      15             : #include <algorithm>
      16             : #include <limits>
      17             : #include <queue>
      18             : #include <set>
      19             : #include <utility>
      20             : #include <time.h>
      21             : 
      22             : #include <cmath>
      23             : #include <ctype.h>  // isalnum
      24             : 
      25             : #include "cpl_error_internal.h"
      26             : #include "gdal_priv.h"
      27             : #include "gdal_pam.h"
      28             : #include "gdal_utils.h"
      29             : #include "cpl_safemaths.hpp"
      30             : #include "memmultidim.h"
      31             : #include "ogrsf_frmts.h"
      32             : #include "gdalmultidim_priv.h"
      33             : 
      34             : #if defined(__clang__) || defined(_MSC_VER)
      35             : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
      36             : #endif
      37             : 
      38             : /************************************************************************/
      39             : /*                       GDALMDArrayUnscaled                            */
      40             : /************************************************************************/
      41             : 
      42             : class GDALMDArrayUnscaled final : public GDALPamMDArray
      43             : {
      44             :   private:
      45             :     std::shared_ptr<GDALMDArray> m_poParent{};
      46             :     const GDALExtendedDataType m_dt;
      47             :     bool m_bHasNoData;
      48             :     const double m_dfScale;
      49             :     const double m_dfOffset;
      50             :     std::vector<GByte> m_abyRawNoData{};
      51             : 
      52             :   protected:
      53          13 :     explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
      54             :                                  double dfScale, double dfOffset,
      55             :                                  double dfOverriddenDstNodata, GDALDataType eDT)
      56          26 :         : GDALAbstractMDArray(std::string(),
      57          26 :                               "Unscaled view of " + poParent->GetFullName()),
      58             :           GDALPamMDArray(
      59          26 :               std::string(), "Unscaled view of " + poParent->GetFullName(),
      60          26 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
      61          13 :           m_poParent(std::move(poParent)),
      62             :           m_dt(GDALExtendedDataType::Create(eDT)),
      63          13 :           m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
      64          78 :           m_dfScale(dfScale), m_dfOffset(dfOffset)
      65             :     {
      66          13 :         m_abyRawNoData.resize(m_dt.GetSize());
      67             :         const auto eNonComplexDT =
      68          13 :             GDALGetNonComplexDataType(m_dt.GetNumericDataType());
      69          26 :         GDALCopyWords64(
      70          13 :             &dfOverriddenDstNodata, GDT_Float64, 0, m_abyRawNoData.data(),
      71             :             eNonComplexDT, GDALGetDataTypeSizeBytes(eNonComplexDT),
      72          13 :             GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
      73          13 :     }
      74             : 
      75             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
      76             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      77             :                const GDALExtendedDataType &bufferDataType,
      78             :                void *pDstBuffer) const override;
      79             : 
      80             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
      81             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      82             :                 const GDALExtendedDataType &bufferDataType,
      83             :                 const void *pSrcBuffer) override;
      84             : 
      85           0 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
      86             :                      CSLConstList papszOptions) const override
      87             :     {
      88           0 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
      89             :     }
      90             : 
      91             :   public:
      92             :     static std::shared_ptr<GDALMDArrayUnscaled>
      93          13 :     Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
      94             :            double dfOffset, double dfDstNodata, GDALDataType eDT)
      95             :     {
      96             :         auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
      97          13 :             poParent, dfScale, dfOffset, dfDstNodata, eDT)));
      98          13 :         newAr->SetSelf(newAr);
      99          13 :         return newAr;
     100             :     }
     101             : 
     102           1 :     bool IsWritable() const override
     103             :     {
     104           1 :         return m_poParent->IsWritable();
     105             :     }
     106             : 
     107          15 :     const std::string &GetFilename() const override
     108             :     {
     109          15 :         return m_poParent->GetFilename();
     110             :     }
     111             : 
     112             :     const std::vector<std::shared_ptr<GDALDimension>> &
     113         220 :     GetDimensions() const override
     114             :     {
     115         220 :         return m_poParent->GetDimensions();
     116             :     }
     117             : 
     118         103 :     const GDALExtendedDataType &GetDataType() const override
     119             :     {
     120         103 :         return m_dt;
     121             :     }
     122             : 
     123           1 :     const std::string &GetUnit() const override
     124             :     {
     125           1 :         return m_poParent->GetUnit();
     126             :     }
     127             : 
     128           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
     129             :     {
     130           1 :         return m_poParent->GetSpatialRef();
     131             :     }
     132             : 
     133           6 :     const void *GetRawNoDataValue() const override
     134             :     {
     135           6 :         return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
     136             :     }
     137             : 
     138           1 :     bool SetRawNoDataValue(const void *pRawNoData) override
     139             :     {
     140           1 :         m_bHasNoData = true;
     141           1 :         memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
     142           1 :         return true;
     143             :     }
     144             : 
     145           4 :     std::vector<GUInt64> GetBlockSize() const override
     146             :     {
     147           4 :         return m_poParent->GetBlockSize();
     148             :     }
     149             : 
     150             :     std::shared_ptr<GDALAttribute>
     151           0 :     GetAttribute(const std::string &osName) const override
     152             :     {
     153           0 :         return m_poParent->GetAttribute(osName);
     154             :     }
     155             : 
     156             :     std::vector<std::shared_ptr<GDALAttribute>>
     157           1 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
     158             :     {
     159           1 :         return m_poParent->GetAttributes(papszOptions);
     160             :     }
     161             : 
     162           0 :     bool SetUnit(const std::string &osUnit) override
     163             :     {
     164           0 :         return m_poParent->SetUnit(osUnit);
     165             :     }
     166             : 
     167           0 :     bool SetSpatialRef(const OGRSpatialReference *poSRS) override
     168             :     {
     169           0 :         return m_poParent->SetSpatialRef(poSRS);
     170             :     }
     171             : 
     172             :     std::shared_ptr<GDALAttribute>
     173           1 :     CreateAttribute(const std::string &osName,
     174             :                     const std::vector<GUInt64> &anDimensions,
     175             :                     const GDALExtendedDataType &oDataType,
     176             :                     CSLConstList papszOptions = nullptr) override
     177             :     {
     178           1 :         return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
     179           1 :                                            papszOptions);
     180             :     }
     181             : };
     182             : 
     183             : /************************************************************************/
     184             : /*                         ~GDALIHasAttribute()                         */
     185             : /************************************************************************/
     186             : 
     187             : GDALIHasAttribute::~GDALIHasAttribute() = default;
     188             : 
     189             : /************************************************************************/
     190             : /*                            GetAttribute()                            */
     191             : /************************************************************************/
     192             : 
     193             : /** Return an attribute by its name.
     194             :  *
     195             :  * If the attribute does not exist, nullptr should be silently returned.
     196             :  *
     197             :  * @note Driver implementation: this method will fallback to
     198             :  * GetAttributeFromAttributes() is not explicitly implemented
     199             :  *
     200             :  * Drivers known to implement it for groups and arrays: MEM, netCDF.
     201             :  *
     202             :  * This is the same as the C function GDALGroupGetAttribute() or
     203             :  * GDALMDArrayGetAttribute().
     204             :  *
     205             :  * @param osName Attribute name
     206             :  * @return the attribute, or nullptr if it does not exist or an error occurred.
     207             :  */
     208             : std::shared_ptr<GDALAttribute>
     209        1022 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
     210             : {
     211        1022 :     return GetAttributeFromAttributes(osName);
     212             : }
     213             : 
     214             : /************************************************************************/
     215             : /*                       GetAttributeFromAttributes()                   */
     216             : /************************************************************************/
     217             : 
     218             : /** Possible fallback implementation for GetAttribute() using GetAttributes().
     219             :  */
     220             : std::shared_ptr<GDALAttribute>
     221        1022 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
     222             : {
     223        2044 :     auto attrs(GetAttributes());
     224        5357 :     for (const auto &attr : attrs)
     225             :     {
     226        5051 :         if (attr->GetName() == osName)
     227         716 :             return attr;
     228             :     }
     229         306 :     return nullptr;
     230             : }
     231             : 
     232             : /************************************************************************/
     233             : /*                           GetAttributes()                            */
     234             : /************************************************************************/
     235             : 
     236             : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
     237             :  *
     238             :  * If the attribute does not exist, nullptr should be silently returned.
     239             :  *
     240             :  * @note Driver implementation: optionally implemented. If implemented,
     241             :  * GetAttribute() should also be implemented.
     242             :  *
     243             :  * Drivers known to implement it for groups and arrays: MEM, netCDF.
     244             :  *
     245             :  * This is the same as the C function GDALGroupGetAttributes() or
     246             :  * GDALMDArrayGetAttributes().
     247             : 
     248             :  * @param papszOptions Driver specific options determining how attributes
     249             :  * should be retrieved. Pass nullptr for default behavior.
     250             :  *
     251             :  * @return the attributes.
     252             :  */
     253             : std::vector<std::shared_ptr<GDALAttribute>>
     254          41 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
     255             : {
     256          41 :     return {};
     257             : }
     258             : 
     259             : /************************************************************************/
     260             : /*                             CreateAttribute()                         */
     261             : /************************************************************************/
     262             : 
     263             : /** Create an attribute within a GDALMDArray or GDALGroup.
     264             :  *
     265             :  * The attribute might not be "physically" created until a value is written
     266             :  * into it.
     267             :  *
     268             :  * Optionally implemented.
     269             :  *
     270             :  * Drivers known to implement it: MEM, netCDF
     271             :  *
     272             :  * This is the same as the C function GDALGroupCreateAttribute() or
     273             :  * GDALMDArrayCreateAttribute()
     274             :  *
     275             :  * @param osName Attribute name.
     276             :  * @param anDimensions List of dimension sizes, ordered from the slowest varying
     277             :  *                     dimension first to the fastest varying dimension last.
     278             :  *                     Empty for a scalar attribute (common case)
     279             :  * @param oDataType  Attribute data type.
     280             :  * @param papszOptions Driver specific options determining how the attribute.
     281             :  * should be created.
     282             :  *
     283             :  * @return the new attribute, or nullptr if case of error
     284             :  */
     285           0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
     286             :     CPL_UNUSED const std::string &osName,
     287             :     CPL_UNUSED const std::vector<GUInt64> &anDimensions,
     288             :     CPL_UNUSED const GDALExtendedDataType &oDataType,
     289             :     CPL_UNUSED CSLConstList papszOptions)
     290             : {
     291           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     292             :              "CreateAttribute() not implemented");
     293           0 :     return nullptr;
     294             : }
     295             : 
     296             : /************************************************************************/
     297             : /*                          DeleteAttribute()                           */
     298             : /************************************************************************/
     299             : 
     300             : /** Delete an attribute from a GDALMDArray or GDALGroup.
     301             :  *
     302             :  * Optionally implemented.
     303             :  *
     304             :  * After this call, if a previously obtained instance of the deleted object
     305             :  * is still alive, no method other than for freeing it should be invoked.
     306             :  *
     307             :  * Drivers known to implement it: MEM, netCDF
     308             :  *
     309             :  * This is the same as the C function GDALGroupDeleteAttribute() or
     310             :  * GDALMDArrayDeleteAttribute()
     311             :  *
     312             :  * @param osName Attribute name.
     313             :  * @param papszOptions Driver specific options determining how the attribute.
     314             :  * should be deleted.
     315             :  *
     316             :  * @return true in case of success
     317             :  * @since GDAL 3.8
     318             :  */
     319           0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
     320             :                                         CPL_UNUSED CSLConstList papszOptions)
     321             : {
     322           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     323             :              "DeleteAttribute() not implemented");
     324           0 :     return false;
     325             : }
     326             : 
     327             : /************************************************************************/
     328             : /*                            GDALGroup()                               */
     329             : /************************************************************************/
     330             : 
     331             : //! @cond Doxygen_Suppress
     332        6694 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
     333        6694 :                      const std::string &osContext)
     334        6694 :     : m_osName(osParentName.empty() ? "/" : osName),
     335             :       m_osFullName(
     336       13388 :           !osParentName.empty()
     337       10278 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
     338             :               : "/"),
     339       16972 :       m_osContext(osContext)
     340             : {
     341        6694 : }
     342             : 
     343             : //! @endcond
     344             : 
     345             : /************************************************************************/
     346             : /*                            ~GDALGroup()                              */
     347             : /************************************************************************/
     348             : 
     349             : GDALGroup::~GDALGroup() = default;
     350             : 
     351             : /************************************************************************/
     352             : /*                          GetMDArrayNames()                           */
     353             : /************************************************************************/
     354             : 
     355             : /** Return the list of multidimensional array names contained in this group.
     356             :  *
     357             :  * @note Driver implementation: optionally implemented. If implemented,
     358             :  * OpenMDArray() should also be implemented.
     359             :  *
     360             :  * Drivers known to implement it: MEM, netCDF.
     361             :  *
     362             :  * This is the same as the C function GDALGroupGetMDArrayNames().
     363             :  *
     364             :  * @param papszOptions Driver specific options determining how arrays
     365             :  * should be retrieved. Pass nullptr for default behavior.
     366             :  *
     367             :  * @return the array names.
     368             :  */
     369             : std::vector<std::string>
     370           0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
     371             : {
     372           0 :     return {};
     373             : }
     374             : 
     375             : /************************************************************************/
     376             : /*                            OpenMDArray()                             */
     377             : /************************************************************************/
     378             : 
     379             : /** Open and return a multidimensional array.
     380             :  *
     381             :  * @note Driver implementation: optionally implemented. If implemented,
     382             :  * GetMDArrayNames() should also be implemented.
     383             :  *
     384             :  * Drivers known to implement it: MEM, netCDF.
     385             :  *
     386             :  * This is the same as the C function GDALGroupOpenMDArray().
     387             :  *
     388             :  * @param osName Array name.
     389             :  * @param papszOptions Driver specific options determining how the array should
     390             :  * be opened.  Pass nullptr for default behavior.
     391             :  *
     392             :  * @return the array, or nullptr.
     393             :  */
     394             : std::shared_ptr<GDALMDArray>
     395           0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
     396             :                        CPL_UNUSED CSLConstList papszOptions) const
     397             : {
     398           0 :     return nullptr;
     399             : }
     400             : 
     401             : /************************************************************************/
     402             : /*                           GetGroupNames()                            */
     403             : /************************************************************************/
     404             : 
     405             : /** Return the list of sub-groups contained in this group.
     406             :  *
     407             :  * @note Driver implementation: optionally implemented. If implemented,
     408             :  * OpenGroup() should also be implemented.
     409             :  *
     410             :  * Drivers known to implement it: MEM, netCDF.
     411             :  *
     412             :  * This is the same as the C function GDALGroupGetGroupNames().
     413             :  *
     414             :  * @param papszOptions Driver specific options determining how groups
     415             :  * should be retrieved. Pass nullptr for default behavior.
     416             :  *
     417             :  * @return the group names.
     418             :  */
     419             : std::vector<std::string>
     420           4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
     421             : {
     422           4 :     return {};
     423             : }
     424             : 
     425             : /************************************************************************/
     426             : /*                             OpenGroup()                              */
     427             : /************************************************************************/
     428             : 
     429             : /** Open and return a sub-group.
     430             :  *
     431             :  * @note Driver implementation: optionally implemented. If implemented,
     432             :  * GetGroupNames() should also be implemented.
     433             :  *
     434             :  * Drivers known to implement it: MEM, netCDF.
     435             :  *
     436             :  * This is the same as the C function GDALGroupOpenGroup().
     437             :  *
     438             :  * @param osName Sub-group name.
     439             :  * @param papszOptions Driver specific options determining how the sub-group
     440             :  * should be opened.  Pass nullptr for default behavior.
     441             :  *
     442             :  * @return the group, or nullptr.
     443             :  */
     444             : std::shared_ptr<GDALGroup>
     445           4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
     446             :                      CPL_UNUSED CSLConstList papszOptions) const
     447             : {
     448           4 :     return nullptr;
     449             : }
     450             : 
     451             : /************************************************************************/
     452             : /*                        GetVectorLayerNames()                         */
     453             : /************************************************************************/
     454             : 
     455             : /** Return the list of layer names contained in this group.
     456             :  *
     457             :  * @note Driver implementation: optionally implemented. If implemented,
     458             :  * OpenVectorLayer() should also be implemented.
     459             :  *
     460             :  * Drivers known to implement it: OpenFileGDB, FileGDB
     461             :  *
     462             :  * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
     463             :  * GDALDataset::GetLayer() should then be used.
     464             :  *
     465             :  * This is the same as the C function GDALGroupGetVectorLayerNames().
     466             :  *
     467             :  * @param papszOptions Driver specific options determining how layers
     468             :  * should be retrieved. Pass nullptr for default behavior.
     469             :  *
     470             :  * @return the vector layer names.
     471             :  * @since GDAL 3.4
     472             :  */
     473             : std::vector<std::string>
     474           1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
     475             : {
     476           1 :     return {};
     477             : }
     478             : 
     479             : /************************************************************************/
     480             : /*                           OpenVectorLayer()                          */
     481             : /************************************************************************/
     482             : 
     483             : /** Open and return a vector layer.
     484             :  *
     485             :  * Due to the historical ownership of OGRLayer* by GDALDataset*, the
     486             :  * lifetime of the returned OGRLayer* is linked to the one of the owner
     487             :  * dataset (contrary to the general design of this class where objects can be
     488             :  * used independently of the object that returned them)
     489             :  *
     490             :  * @note Driver implementation: optionally implemented. If implemented,
     491             :  * GetVectorLayerNames() should also be implemented.
     492             :  *
     493             :  * Drivers known to implement it: MEM, netCDF.
     494             :  *
     495             :  * This is the same as the C function GDALGroupOpenVectorLayer().
     496             :  *
     497             :  * @param osName Vector layer name.
     498             :  * @param papszOptions Driver specific options determining how the layer should
     499             :  * be opened.  Pass nullptr for default behavior.
     500             :  *
     501             :  * @return the group, or nullptr.
     502             :  */
     503           2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
     504             :                                      CPL_UNUSED CSLConstList papszOptions) const
     505             : {
     506           2 :     return nullptr;
     507             : }
     508             : 
     509             : /************************************************************************/
     510             : /*                             GetDimensions()                          */
     511             : /************************************************************************/
     512             : 
     513             : /** Return the list of dimensions contained in this group and used by its
     514             :  * arrays.
     515             :  *
     516             :  * This is for dimensions that can potentially be used by several arrays.
     517             :  * Not all drivers might implement this. To retrieve the dimensions used by
     518             :  * a specific array, use GDALMDArray::GetDimensions().
     519             :  *
     520             :  * Drivers known to implement it: MEM, netCDF
     521             :  *
     522             :  * This is the same as the C function GDALGroupGetDimensions().
     523             :  *
     524             :  * @param papszOptions Driver specific options determining how groups
     525             :  * should be retrieved. Pass nullptr for default behavior.
     526             :  *
     527             :  * @return the dimensions.
     528             :  */
     529             : std::vector<std::shared_ptr<GDALDimension>>
     530          11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
     531             : {
     532          11 :     return {};
     533             : }
     534             : 
     535             : /************************************************************************/
     536             : /*                         GetStructuralInfo()                          */
     537             : /************************************************************************/
     538             : 
     539             : /** Return structural information on the group.
     540             :  *
     541             :  * This may be the compression, etc..
     542             :  *
     543             :  * The return value should not be freed and is valid until GDALGroup is
     544             :  * released or this function called again.
     545             :  *
     546             :  * This is the same as the C function GDALGroupGetStructuralInfo().
     547             :  */
     548          29 : CSLConstList GDALGroup::GetStructuralInfo() const
     549             : {
     550          29 :     return nullptr;
     551             : }
     552             : 
     553             : /************************************************************************/
     554             : /*                              CreateGroup()                           */
     555             : /************************************************************************/
     556             : 
     557             : /** Create a sub-group within a group.
     558             :  *
     559             :  * Optionally implemented by drivers.
     560             :  *
     561             :  * Drivers known to implement it: MEM, netCDF
     562             :  *
     563             :  * This is the same as the C function GDALGroupCreateGroup().
     564             :  *
     565             :  * @param osName Sub-group name.
     566             :  * @param papszOptions Driver specific options determining how the sub-group
     567             :  * should be created.
     568             :  *
     569             :  * @return the new sub-group, or nullptr in case of error.
     570             :  */
     571             : std::shared_ptr<GDALGroup>
     572           0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
     573             :                        CPL_UNUSED CSLConstList papszOptions)
     574             : {
     575           0 :     CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
     576           0 :     return nullptr;
     577             : }
     578             : 
     579             : /************************************************************************/
     580             : /*                          DeleteGroup()                               */
     581             : /************************************************************************/
     582             : 
     583             : /** Delete a sub-group from a group.
     584             :  *
     585             :  * Optionally implemented.
     586             :  *
     587             :  * After this call, if a previously obtained instance of the deleted object
     588             :  * is still alive, no method other than for freeing it should be invoked.
     589             :  *
     590             :  * Drivers known to implement it: MEM, Zarr
     591             :  *
     592             :  * This is the same as the C function GDALGroupDeleteGroup().
     593             :  *
     594             :  * @param osName Sub-group name.
     595             :  * @param papszOptions Driver specific options determining how the group.
     596             :  * should be deleted.
     597             :  *
     598             :  * @return true in case of success
     599             :  * @since GDAL 3.8
     600             :  */
     601           0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
     602             :                             CPL_UNUSED CSLConstList papszOptions)
     603             : {
     604           0 :     CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
     605           0 :     return false;
     606             : }
     607             : 
     608             : /************************************************************************/
     609             : /*                            CreateDimension()                         */
     610             : /************************************************************************/
     611             : 
     612             : /** Create a dimension within a group.
     613             :  *
     614             :  * @note Driver implementation: drivers supporting CreateDimension() should
     615             :  * implement this method, but do not have necessarily to implement
     616             :  * GDALGroup::GetDimensions().
     617             :  *
     618             :  * Drivers known to implement it: MEM, netCDF
     619             :  *
     620             :  * This is the same as the C function GDALGroupCreateDimension().
     621             :  *
     622             :  * @param osName Dimension name.
     623             :  * @param osType Dimension type (might be empty, and ignored by drivers)
     624             :  * @param osDirection Dimension direction (might be empty, and ignored by
     625             :  * drivers)
     626             :  * @param nSize  Number of values indexed by this dimension. Should be > 0.
     627             :  * @param papszOptions Driver specific options determining how the dimension
     628             :  * should be created.
     629             :  *
     630             :  * @return the new dimension, or nullptr if case of error
     631             :  */
     632           0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
     633             :     CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
     634             :     CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
     635             :     CPL_UNUSED CSLConstList papszOptions)
     636             : {
     637           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     638             :              "CreateDimension() not implemented");
     639           0 :     return nullptr;
     640             : }
     641             : 
     642             : /************************************************************************/
     643             : /*                             CreateMDArray()                          */
     644             : /************************************************************************/
     645             : 
     646             : /** Create a multidimensional array within a group.
     647             :  *
     648             :  * It is recommended that the GDALDimension objects passed in aoDimensions
     649             :  * belong to this group, either by retrieving them with GetDimensions()
     650             :  * or creating a new one with CreateDimension().
     651             :  *
     652             :  * Optionally implemented.
     653             :  *
     654             :  * Drivers known to implement it: MEM, netCDF
     655             :  *
     656             :  * This is the same as the C function GDALGroupCreateMDArray().
     657             :  *
     658             :  * @note Driver implementation: drivers should take into account the possibility
     659             :  * that GDALDimension object passed in aoDimensions might belong to a different
     660             :  * group / dataset / driver and act accordingly.
     661             :  *
     662             :  * @param osName Array name.
     663             :  * @param aoDimensions List of dimensions, ordered from the slowest varying
     664             :  *                     dimension first to the fastest varying dimension last.
     665             :  *                     Might be empty for a scalar array (if supported by
     666             :  * driver)
     667             :  * @param oDataType  Array data type.
     668             :  * @param papszOptions Driver specific options determining how the array
     669             :  * should be created.
     670             :  *
     671             :  * @return the new array, or nullptr if case of error
     672             :  */
     673           0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
     674             :     CPL_UNUSED const std::string &osName,
     675             :     CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
     676             :     CPL_UNUSED const GDALExtendedDataType &oDataType,
     677             :     CPL_UNUSED CSLConstList papszOptions)
     678             : {
     679           0 :     CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
     680           0 :     return nullptr;
     681             : }
     682             : 
     683             : /************************************************************************/
     684             : /*                          DeleteMDArray()                             */
     685             : /************************************************************************/
     686             : 
     687             : /** Delete an array from a group.
     688             :  *
     689             :  * Optionally implemented.
     690             :  *
     691             :  * After this call, if a previously obtained instance of the deleted object
     692             :  * is still alive, no method other than for freeing it should be invoked.
     693             :  *
     694             :  * Drivers known to implement it: MEM, Zarr
     695             :  *
     696             :  * This is the same as the C function GDALGroupDeleteMDArray().
     697             :  *
     698             :  * @param osName Arrayname.
     699             :  * @param papszOptions Driver specific options determining how the array.
     700             :  * should be deleted.
     701             :  *
     702             :  * @return true in case of success
     703             :  * @since GDAL 3.8
     704             :  */
     705           0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
     706             :                               CPL_UNUSED CSLConstList papszOptions)
     707             : {
     708           0 :     CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
     709           0 :     return false;
     710             : }
     711             : 
     712             : /************************************************************************/
     713             : /*                           GetTotalCopyCost()                         */
     714             : /************************************************************************/
     715             : 
     716             : /** Return a total "cost" to copy the group.
     717             :  *
     718             :  * Used as a parameter for CopFrom()
     719             :  */
     720          22 : GUInt64 GDALGroup::GetTotalCopyCost() const
     721             : {
     722          22 :     GUInt64 nCost = COPY_COST;
     723          22 :     nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
     724             : 
     725          44 :     auto groupNames = GetGroupNames();
     726          26 :     for (const auto &name : groupNames)
     727             :     {
     728           8 :         auto subGroup = OpenGroup(name);
     729           4 :         if (subGroup)
     730             :         {
     731           4 :             nCost += subGroup->GetTotalCopyCost();
     732             :         }
     733             :     }
     734             : 
     735          22 :     auto arrayNames = GetMDArrayNames();
     736          61 :     for (const auto &name : arrayNames)
     737             :     {
     738          78 :         auto array = OpenMDArray(name);
     739          39 :         if (array)
     740             :         {
     741          39 :             nCost += array->GetTotalCopyCost();
     742             :         }
     743             :     }
     744          44 :     return nCost;
     745             : }
     746             : 
     747             : /************************************************************************/
     748             : /*                               CopyFrom()                             */
     749             : /************************************************************************/
     750             : 
     751             : /** Copy the content of a group into a new (generally empty) group.
     752             :  *
     753             :  * @param poDstRootGroup Destination root group. Must NOT be nullptr.
     754             :  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
     755             :  *                   of some output drivers this is not recommended)
     756             :  * @param poSrcGroup Source group. Must NOT be nullptr.
     757             :  * @param bStrict Whether to enable strict mode. In strict mode, any error will
     758             :  *                stop the copy. In relaxed mode, the copy will be attempted to
     759             :  *                be pursued.
     760             :  * @param nCurCost  Should be provided as a variable initially set to 0.
     761             :  * @param nTotalCost Total cost from GetTotalCopyCost().
     762             :  * @param pfnProgress Progress callback, or nullptr.
     763             :  * @param pProgressData Progress user data, or nulptr.
     764             :  * @param papszOptions Creation options. Currently, only array creation
     765             :  *                     options are supported. They must be prefixed with
     766             :  * "ARRAY:" . The scope may be further restricted to arrays of a certain
     767             :  *                     dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
     768             :  *                     For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
     769             :  *                     restrict BLOCKSIZE=256,256 to arrays of dimension 2.
     770             :  *                     Restriction to arrays of a given name is done with adding
     771             :  *                     "IF(NAME={name}):" after "ARRAY:". {name} can also be
     772             :  *                     a full qualified name.
     773             :  *                     A non-driver specific ARRAY option, "AUTOSCALE=YES" can
     774             :  * be used to ask (non indexing) variables of type Float32 or Float64 to be
     775             :  * scaled to UInt16 with scale and offset values being computed from the minimum
     776             :  * and maximum of the source array. The integer data type used can be set with
     777             :  *                     AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
     778             :  *
     779             :  * @return true in case of success (or partial success if bStrict == false).
     780             :  */
     781          22 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
     782             :                          GDALDataset *poSrcDS,
     783             :                          const std::shared_ptr<GDALGroup> &poSrcGroup,
     784             :                          bool bStrict, GUInt64 &nCurCost,
     785             :                          const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
     786             :                          void *pProgressData, CSLConstList papszOptions)
     787             : {
     788          22 :     if (pfnProgress == nullptr)
     789           0 :         pfnProgress = GDALDummyProgress;
     790             : 
     791             : #define EXIT_OR_CONTINUE_IF_NULL(x)                                            \
     792             :     if (!(x))                                                                  \
     793             :     {                                                                          \
     794             :         if (bStrict)                                                           \
     795             :             return false;                                                      \
     796             :         continue;                                                              \
     797             :     }                                                                          \
     798             :     (void)0
     799             : 
     800             :     try
     801             :     {
     802          22 :         nCurCost += GDALGroup::COPY_COST;
     803             : 
     804          44 :         const auto srcDims = poSrcGroup->GetDimensions();
     805             :         std::map<std::string, std::shared_ptr<GDALDimension>>
     806          44 :             mapExistingDstDims;
     807          44 :         std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
     808          56 :         for (const auto &dim : srcDims)
     809             :         {
     810             :             auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
     811          34 :                                           dim->GetDirection(), dim->GetSize());
     812          34 :             EXIT_OR_CONTINUE_IF_NULL(dstDim);
     813          34 :             mapExistingDstDims[dim->GetName()] = std::move(dstDim);
     814          68 :             auto poIndexingVarSrc(dim->GetIndexingVariable());
     815          34 :             if (poIndexingVarSrc)
     816             :             {
     817             :                 mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
     818          16 :                                                        ->GetName()] =
     819          32 :                     dim->GetName();
     820             :             }
     821             :         }
     822             : 
     823          44 :         auto attrs = poSrcGroup->GetAttributes();
     824          28 :         for (const auto &attr : attrs)
     825             :         {
     826             :             auto dstAttr =
     827           6 :                 CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
     828          12 :                                 attr->GetDataType());
     829           6 :             EXIT_OR_CONTINUE_IF_NULL(dstAttr);
     830           6 :             auto raw(attr->ReadAsRaw());
     831           6 :             if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
     832           0 :                 return false;
     833             :         }
     834          22 :         if (!attrs.empty())
     835             :         {
     836           4 :             nCurCost += attrs.size() * GDALAttribute::COPY_COST;
     837           4 :             if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
     838           0 :                 return false;
     839             :         }
     840             : 
     841             :         const auto CopyArray =
     842          39 :             [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
     843             :              &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
     844             :              papszOptions, bStrict, &nCurCost,
     845         355 :              nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
     846             :         {
     847             :             // Map source dimensions to target dimensions
     848          78 :             std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
     849          39 :             const auto &srcArrayDims(srcArray->GetDimensions());
     850          99 :             for (const auto &dim : srcArrayDims)
     851             :             {
     852             :                 auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
     853          60 :                     dim->GetFullName());
     854          60 :                 if (dstDim && dstDim->GetSize() == dim->GetSize())
     855             :                 {
     856          50 :                     dstArrayDims.emplace_back(dstDim);
     857             :                 }
     858             :                 else
     859             :                 {
     860          10 :                     auto oIter = mapExistingDstDims.find(dim->GetName());
     861          19 :                     if (oIter != mapExistingDstDims.end() &&
     862           9 :                         oIter->second->GetSize() == dim->GetSize())
     863             :                     {
     864           8 :                         dstArrayDims.emplace_back(oIter->second);
     865             :                     }
     866             :                     else
     867             :                     {
     868           2 :                         std::string newDimName;
     869           2 :                         if (oIter == mapExistingDstDims.end())
     870             :                         {
     871           1 :                             newDimName = dim->GetName();
     872             :                         }
     873             :                         else
     874             :                         {
     875           1 :                             std::string newDimNamePrefix(srcArray->GetName() +
     876           3 :                                                          '_' + dim->GetName());
     877           1 :                             newDimName = newDimNamePrefix;
     878           1 :                             int nIterCount = 2;
     879           0 :                             while (
     880           1 :                                 cpl::contains(mapExistingDstDims, newDimName))
     881             :                             {
     882           0 :                                 newDimName = newDimNamePrefix +
     883           0 :                                              CPLSPrintf("_%d", nIterCount);
     884           0 :                                 nIterCount++;
     885             :                             }
     886             :                         }
     887           4 :                         dstDim = CreateDimension(newDimName, dim->GetType(),
     888             :                                                  dim->GetDirection(),
     889           4 :                                                  dim->GetSize());
     890           2 :                         if (!dstDim)
     891           0 :                             return false;
     892           2 :                         mapExistingDstDims[newDimName] = dstDim;
     893           2 :                         dstArrayDims.emplace_back(dstDim);
     894             :                     }
     895             :                 }
     896             :             }
     897             : 
     898          78 :             CPLStringList aosArrayCO;
     899          39 :             bool bAutoScale = false;
     900          39 :             GDALDataType eAutoScaleType = GDT_UInt16;
     901          46 :             for (const char *pszItem : cpl::Iterate(papszOptions))
     902             :             {
     903           7 :                 if (STARTS_WITH_CI(pszItem, "ARRAY:"))
     904             :                 {
     905           7 :                     const char *pszOption = pszItem + strlen("ARRAY:");
     906           7 :                     if (STARTS_WITH_CI(pszOption, "IF(DIM="))
     907             :                     {
     908           1 :                         const char *pszNext = strchr(pszOption, ':');
     909           1 :                         if (pszNext != nullptr)
     910             :                         {
     911           1 :                             int nDim = atoi(pszOption + strlen("IF(DIM="));
     912           1 :                             if (static_cast<size_t>(nDim) ==
     913           1 :                                 dstArrayDims.size())
     914             :                             {
     915           1 :                                 pszOption = pszNext + 1;
     916             :                             }
     917             :                             else
     918             :                             {
     919           0 :                                 pszOption = nullptr;
     920             :                             }
     921             :                         }
     922             :                     }
     923           6 :                     else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
     924             :                     {
     925           2 :                         const char *pszName = pszOption + strlen("IF(NAME=");
     926           2 :                         const char *pszNext = strchr(pszName, ':');
     927           2 :                         if (pszNext != nullptr && pszNext > pszName &&
     928           2 :                             pszNext[-1] == ')')
     929             :                         {
     930           4 :                             CPLString osName;
     931           2 :                             osName.assign(pszName, pszNext - pszName - 1);
     932           3 :                             if (osName == srcArray->GetName() ||
     933           1 :                                 osName == srcArray->GetFullName())
     934             :                             {
     935           2 :                                 pszOption = pszNext + 1;
     936             :                             }
     937             :                             else
     938             :                             {
     939           0 :                                 pszOption = nullptr;
     940             :                             }
     941             :                         }
     942             :                     }
     943           7 :                     if (pszOption)
     944             :                     {
     945           7 :                         if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
     946             :                         {
     947             :                             bAutoScale =
     948           2 :                                 CPLTestBool(pszOption + strlen("AUTOSCALE="));
     949             :                         }
     950           5 :                         else if (STARTS_WITH_CI(pszOption,
     951             :                                                 "AUTOSCALE_DATA_TYPE="))
     952             :                         {
     953           1 :                             const char *pszDataType =
     954             :                                 pszOption + strlen("AUTOSCALE_DATA_TYPE=");
     955           1 :                             eAutoScaleType = GDALGetDataTypeByName(pszDataType);
     956           2 :                             if (GDALDataTypeIsComplex(eAutoScaleType) ||
     957           1 :                                 GDALDataTypeIsFloating(eAutoScaleType))
     958             :                             {
     959           0 :                                 CPLError(CE_Failure, CPLE_NotSupported,
     960             :                                          "Unsupported value for "
     961             :                                          "AUTOSCALE_DATA_TYPE");
     962           0 :                                 return false;
     963             :                             }
     964             :                         }
     965             :                         else
     966             :                         {
     967           4 :                             aosArrayCO.AddString(pszOption);
     968             :                         }
     969             :                     }
     970             :                 }
     971             :             }
     972             : 
     973             :             auto oIterDimName =
     974          39 :                 mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
     975          39 :             const auto &srcArrayType = srcArray->GetDataType();
     976             : 
     977          39 :             std::shared_ptr<GDALMDArray> dstArray;
     978             : 
     979             :             // Only autoscale non-indexing variables
     980          39 :             bool bHasOffset = false;
     981          39 :             bool bHasScale = false;
     982           4 :             if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
     983           2 :                 (srcArrayType.GetNumericDataType() == GDT_Float32 ||
     984           0 :                  srcArrayType.GetNumericDataType() == GDT_Float64) &&
     985           2 :                 srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
     986          43 :                 srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
     987          41 :                 oIterDimName == mapSrcVariableNameToIndexedDimName.end())
     988             :             {
     989           2 :                 constexpr bool bApproxOK = false;
     990           2 :                 constexpr bool bForce = true;
     991           2 :                 double dfMin = 0.0;
     992           2 :                 double dfMax = 0.0;
     993           2 :                 if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
     994             :                                             nullptr, nullptr, nullptr, nullptr,
     995           2 :                                             nullptr) != CE_None)
     996             :                 {
     997           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     998             :                              "Could not retrieve statistics for array %s",
     999           0 :                              srcArray->GetName().c_str());
    1000           0 :                     return false;
    1001             :                 }
    1002           2 :                 double dfDTMin = 0;
    1003           2 :                 double dfDTMax = 0;
    1004             : #define setDTMinMax(ctype)                                                     \
    1005             :     do                                                                         \
    1006             :     {                                                                          \
    1007             :         dfDTMin = static_cast<double>(std::numeric_limits<ctype>::min());      \
    1008             :         dfDTMax = static_cast<double>(std::numeric_limits<ctype>::max());      \
    1009             :     } while (0)
    1010             : 
    1011           2 :                 switch (eAutoScaleType)
    1012             :                 {
    1013           0 :                     case GDT_Byte:
    1014           0 :                         setDTMinMax(GByte);
    1015           0 :                         break;
    1016           0 :                     case GDT_Int8:
    1017           0 :                         setDTMinMax(GInt8);
    1018           0 :                         break;
    1019           1 :                     case GDT_UInt16:
    1020           1 :                         setDTMinMax(GUInt16);
    1021           1 :                         break;
    1022           1 :                     case GDT_Int16:
    1023           1 :                         setDTMinMax(GInt16);
    1024           1 :                         break;
    1025           0 :                     case GDT_UInt32:
    1026           0 :                         setDTMinMax(GUInt32);
    1027           0 :                         break;
    1028           0 :                     case GDT_Int32:
    1029           0 :                         setDTMinMax(GInt32);
    1030           0 :                         break;
    1031           0 :                     case GDT_UInt64:
    1032           0 :                         setDTMinMax(std::uint64_t);
    1033           0 :                         break;
    1034           0 :                     case GDT_Int64:
    1035           0 :                         setDTMinMax(std::int64_t);
    1036           0 :                         break;
    1037           0 :                     case GDT_Float32:
    1038             :                     case GDT_Float64:
    1039             :                     case GDT_Unknown:
    1040             :                     case GDT_CInt16:
    1041             :                     case GDT_CInt32:
    1042             :                     case GDT_CFloat32:
    1043             :                     case GDT_CFloat64:
    1044             :                     case GDT_TypeCount:
    1045           0 :                         CPLAssert(false);
    1046             :                 }
    1047             : 
    1048             :                 dstArray =
    1049           4 :                     CreateMDArray(srcArray->GetName(), dstArrayDims,
    1050           4 :                                   GDALExtendedDataType::Create(eAutoScaleType),
    1051           4 :                                   aosArrayCO.List());
    1052           2 :                 if (!dstArray)
    1053           0 :                     return !bStrict;
    1054             : 
    1055           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1056             :                 {
    1057             :                     // If there's a nodata value in the source array, reserve
    1058             :                     // DTMax for that purpose in the target scaled array
    1059           1 :                     if (!dstArray->SetNoDataValue(dfDTMax))
    1060             :                     {
    1061           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    1062             :                                  "Cannot set nodata value");
    1063           0 :                         return false;
    1064             :                     }
    1065           1 :                     dfDTMax--;
    1066             :                 }
    1067           2 :                 const double dfScale =
    1068           2 :                     dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
    1069           2 :                 const double dfOffset = dfMin - dfDTMin * dfScale;
    1070             : 
    1071           4 :                 if (!dstArray->SetOffset(dfOffset) ||
    1072           2 :                     !dstArray->SetScale(dfScale))
    1073             :                 {
    1074           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1075             :                              "Cannot set scale/offset");
    1076           0 :                     return false;
    1077             :                 }
    1078             : 
    1079           2 :                 auto poUnscaled = dstArray->GetUnscaled();
    1080           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1081             :                 {
    1082           1 :                     poUnscaled->SetNoDataValue(
    1083             :                         srcArray->GetNoDataValueAsDouble());
    1084             :                 }
    1085             : 
    1086             :                 // Copy source array into unscaled array
    1087           4 :                 if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1088             :                                           nCurCost, nTotalCost, pfnProgress,
    1089           2 :                                           pProgressData))
    1090             :                 {
    1091           0 :                     return false;
    1092             :                 }
    1093             :             }
    1094             :             else
    1095             :             {
    1096          74 :                 dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
    1097          74 :                                          srcArrayType, aosArrayCO.List());
    1098          37 :                 if (!dstArray)
    1099           0 :                     return !bStrict;
    1100             : 
    1101          74 :                 if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1102             :                                         nCurCost, nTotalCost, pfnProgress,
    1103          37 :                                         pProgressData))
    1104             :                 {
    1105           0 :                     return false;
    1106             :                 }
    1107             :             }
    1108             : 
    1109             :             // If this array is the indexing variable of a dimension, link them
    1110             :             // together.
    1111          39 :             if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
    1112             :             {
    1113             :                 auto oCorrespondingDimIter =
    1114          16 :                     mapExistingDstDims.find(oIterDimName->second);
    1115          16 :                 if (oCorrespondingDimIter != mapExistingDstDims.end())
    1116             :                 {
    1117             :                     CPLErrorStateBackuper oErrorStateBackuper(
    1118          16 :                         CPLQuietErrorHandler);
    1119          32 :                     oCorrespondingDimIter->second->SetIndexingVariable(
    1120          16 :                         std::move(dstArray));
    1121             :                 }
    1122             :             }
    1123             : 
    1124          39 :             return true;
    1125          22 :         };
    1126             : 
    1127          44 :         const auto arrayNames = poSrcGroup->GetMDArrayNames();
    1128             : 
    1129             :         // Start by copying arrays that are indexing variables of dimensions
    1130          61 :         for (const auto &name : arrayNames)
    1131             :         {
    1132          39 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1133          39 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1134             : 
    1135          39 :             if (cpl::contains(mapSrcVariableNameToIndexedDimName,
    1136          39 :                               srcArray->GetName()))
    1137             :             {
    1138          16 :                 if (!CopyArray(srcArray))
    1139           0 :                     return false;
    1140             :             }
    1141             :         }
    1142             : 
    1143             :         // Then copy regular arrays
    1144          61 :         for (const auto &name : arrayNames)
    1145             :         {
    1146          39 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1147          39 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1148             : 
    1149          39 :             if (!cpl::contains(mapSrcVariableNameToIndexedDimName,
    1150          39 :                                srcArray->GetName()))
    1151             :             {
    1152          23 :                 if (!CopyArray(srcArray))
    1153           0 :                     return false;
    1154             :             }
    1155             :         }
    1156             : 
    1157          44 :         const auto groupNames = poSrcGroup->GetGroupNames();
    1158          26 :         for (const auto &name : groupNames)
    1159             :         {
    1160           4 :             auto srcSubGroup = poSrcGroup->OpenGroup(name);
    1161           4 :             EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
    1162           4 :             auto dstSubGroup = CreateGroup(name);
    1163           4 :             EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
    1164           8 :             if (!dstSubGroup->CopyFrom(
    1165             :                     poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
    1166           4 :                     nTotalCost, pfnProgress, pProgressData, papszOptions))
    1167           0 :                 return false;
    1168             :         }
    1169             : 
    1170          22 :         if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    1171           0 :             return false;
    1172             : 
    1173          22 :         return true;
    1174             :     }
    1175           0 :     catch (const std::exception &e)
    1176             :     {
    1177           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1178           0 :         return false;
    1179             :     }
    1180             : }
    1181             : 
    1182             : /************************************************************************/
    1183             : /*                         GetInnerMostGroup()                          */
    1184             : /************************************************************************/
    1185             : 
    1186             : //! @cond Doxygen_Suppress
    1187             : const GDALGroup *
    1188        1051 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
    1189             :                              std::shared_ptr<GDALGroup> &curGroupHolder,
    1190             :                              std::string &osLastPart) const
    1191             : {
    1192        1051 :     if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
    1193           6 :         return nullptr;
    1194        1045 :     const GDALGroup *poCurGroup = this;
    1195             :     CPLStringList aosTokens(
    1196        2090 :         CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
    1197        1045 :     if (aosTokens.size() == 0)
    1198             :     {
    1199           0 :         return nullptr;
    1200             :     }
    1201             : 
    1202        1380 :     for (int i = 0; i < aosTokens.size() - 1; i++)
    1203             :     {
    1204         343 :         curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
    1205         343 :         if (!curGroupHolder)
    1206             :         {
    1207           8 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
    1208             :                      aosTokens[i]);
    1209           8 :             return nullptr;
    1210             :         }
    1211         335 :         poCurGroup = curGroupHolder.get();
    1212             :     }
    1213        1037 :     osLastPart = aosTokens[aosTokens.size() - 1];
    1214        1037 :     return poCurGroup;
    1215             : }
    1216             : 
    1217             : //! @endcond
    1218             : 
    1219             : /************************************************************************/
    1220             : /*                      OpenMDArrayFromFullname()                       */
    1221             : /************************************************************************/
    1222             : 
    1223             : /** Get an array from its fully qualified name */
    1224             : std::shared_ptr<GDALMDArray>
    1225         364 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
    1226             :                                    CSLConstList papszOptions) const
    1227             : {
    1228         728 :     std::string osName;
    1229         364 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1230         364 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1231         364 :     if (poGroup == nullptr)
    1232          10 :         return nullptr;
    1233         354 :     return poGroup->OpenMDArray(osName, papszOptions);
    1234             : }
    1235             : 
    1236             : /************************************************************************/
    1237             : /*                          ResolveMDArray()                            */
    1238             : /************************************************************************/
    1239             : 
    1240             : /** Locate an array in a group and its subgroups by name.
    1241             :  *
    1242             :  * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
    1243             :  * used
    1244             :  * Otherwise the search will start from the group identified by osStartingPath,
    1245             :  * and an array whose name is osName will be looked for in this group (if
    1246             :  * osStartingPath is empty or "/", then the current group is used). If there
    1247             :  * is no match, then a recursive descendent search will be made in its
    1248             :  * subgroups. If there is no match in the subgroups, then the parent (if
    1249             :  * existing) of the group pointed by osStartingPath will be used as the new
    1250             :  * starting point for the search.
    1251             :  *
    1252             :  * @param osName name, qualified or not
    1253             :  * @param osStartingPath fully qualified name of the (sub-)group from which
    1254             :  *                       the search should be started. If this is a non-empty
    1255             :  *                       string, the group on which this method is called should
    1256             :  *                       nominally be the root group (otherwise the path will
    1257             :  *                       be interpreted as from the current group)
    1258             :  * @param papszOptions options to pass to OpenMDArray()
    1259             :  * @since GDAL 3.2
    1260             :  */
    1261             : std::shared_ptr<GDALMDArray>
    1262          19 : GDALGroup::ResolveMDArray(const std::string &osName,
    1263             :                           const std::string &osStartingPath,
    1264             :                           CSLConstList papszOptions) const
    1265             : {
    1266          19 :     if (!osName.empty() && osName[0] == '/')
    1267             :     {
    1268           1 :         auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
    1269           1 :         if (poArray)
    1270           1 :             return poArray;
    1271             :     }
    1272          36 :     std::string osPath(osStartingPath);
    1273          36 :     std::set<std::string> oSetAlreadyVisited;
    1274             : 
    1275             :     while (true)
    1276             :     {
    1277           0 :         std::shared_ptr<GDALGroup> curGroupHolder;
    1278           0 :         std::shared_ptr<GDALGroup> poGroup;
    1279             : 
    1280          22 :         std::queue<std::shared_ptr<GDALGroup>> oQueue;
    1281          22 :         bool goOn = false;
    1282          22 :         if (osPath.empty() || osPath == "/")
    1283             :         {
    1284          11 :             goOn = true;
    1285             :         }
    1286             :         else
    1287             :         {
    1288          22 :             std::string osLastPart;
    1289             :             const GDALGroup *poGroupPtr =
    1290          11 :                 GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
    1291          11 :             if (poGroupPtr)
    1292          11 :                 poGroup = poGroupPtr->OpenGroup(osLastPart);
    1293          22 :             if (poGroup &&
    1294          22 :                 !cpl::contains(oSetAlreadyVisited, poGroup->GetFullName()))
    1295             :             {
    1296          11 :                 oQueue.push(poGroup);
    1297          11 :                 goOn = true;
    1298             :             }
    1299             :         }
    1300             : 
    1301          22 :         if (goOn)
    1302             :         {
    1303          17 :             do
    1304             :             {
    1305             :                 const GDALGroup *groupPtr;
    1306          39 :                 if (!oQueue.empty())
    1307             :                 {
    1308          28 :                     poGroup = oQueue.front();
    1309          28 :                     oQueue.pop();
    1310          28 :                     groupPtr = poGroup.get();
    1311             :                 }
    1312             :                 else
    1313             :                 {
    1314          11 :                     groupPtr = this;
    1315             :                 }
    1316             : 
    1317          39 :                 auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
    1318          39 :                 if (poArray)
    1319          16 :                     return poArray;
    1320             : 
    1321          46 :                 const auto aosGroupNames = groupPtr->GetGroupNames();
    1322          47 :                 for (const auto &osGroupName : aosGroupNames)
    1323             :                 {
    1324          48 :                     auto poSubGroup = groupPtr->OpenGroup(osGroupName);
    1325          48 :                     if (poSubGroup && !cpl::contains(oSetAlreadyVisited,
    1326          48 :                                                      poSubGroup->GetFullName()))
    1327             :                     {
    1328          24 :                         oQueue.push(poSubGroup);
    1329          24 :                         oSetAlreadyVisited.insert(poSubGroup->GetFullName());
    1330             :                     }
    1331             :                 }
    1332          23 :             } while (!oQueue.empty());
    1333             :         }
    1334             : 
    1335           6 :         if (osPath.empty() || osPath == "/")
    1336           2 :             break;
    1337             : 
    1338           4 :         const auto nPos = osPath.rfind('/');
    1339           4 :         if (nPos == 0)
    1340           1 :             osPath = "/";
    1341             :         else
    1342             :         {
    1343           3 :             if (nPos == std::string::npos)
    1344           0 :                 break;
    1345           3 :             osPath.resize(nPos);
    1346             :         }
    1347           4 :     }
    1348           2 :     return nullptr;
    1349             : }
    1350             : 
    1351             : /************************************************************************/
    1352             : /*                       OpenGroupFromFullname()                        */
    1353             : /************************************************************************/
    1354             : 
    1355             : /** Get a group from its fully qualified name.
    1356             :  * @since GDAL 3.2
    1357             :  */
    1358             : std::shared_ptr<GDALGroup>
    1359         551 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
    1360             :                                  CSLConstList papszOptions) const
    1361             : {
    1362        1102 :     std::string osName;
    1363         551 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1364         551 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1365         551 :     if (poGroup == nullptr)
    1366           2 :         return nullptr;
    1367         549 :     return poGroup->OpenGroup(osName, papszOptions);
    1368             : }
    1369             : 
    1370             : /************************************************************************/
    1371             : /*                      OpenDimensionFromFullname()                     */
    1372             : /************************************************************************/
    1373             : 
    1374             : /** Get a dimension from its fully qualified name */
    1375             : std::shared_ptr<GDALDimension>
    1376         125 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
    1377             : {
    1378         250 :     std::string osName;
    1379         125 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1380         125 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1381         125 :     if (poGroup == nullptr)
    1382           2 :         return nullptr;
    1383         246 :     auto dims(poGroup->GetDimensions());
    1384         203 :     for (auto &dim : dims)
    1385             :     {
    1386         164 :         if (dim->GetName() == osName)
    1387          84 :             return dim;
    1388             :     }
    1389          39 :     return nullptr;
    1390             : }
    1391             : 
    1392             : /************************************************************************/
    1393             : /*                           ClearStatistics()                          */
    1394             : /************************************************************************/
    1395             : 
    1396             : /**
    1397             :  * \brief Clear statistics.
    1398             :  *
    1399             :  * @since GDAL 3.4
    1400             :  */
    1401           0 : void GDALGroup::ClearStatistics()
    1402             : {
    1403           0 :     auto groupNames = GetGroupNames();
    1404           0 :     for (const auto &name : groupNames)
    1405             :     {
    1406           0 :         auto subGroup = OpenGroup(name);
    1407           0 :         if (subGroup)
    1408             :         {
    1409           0 :             subGroup->ClearStatistics();
    1410             :         }
    1411             :     }
    1412             : 
    1413           0 :     auto arrayNames = GetMDArrayNames();
    1414           0 :     for (const auto &name : arrayNames)
    1415             :     {
    1416           0 :         auto array = OpenMDArray(name);
    1417           0 :         if (array)
    1418             :         {
    1419           0 :             array->ClearStatistics();
    1420             :         }
    1421             :     }
    1422           0 : }
    1423             : 
    1424             : /************************************************************************/
    1425             : /*                            Rename()                                  */
    1426             : /************************************************************************/
    1427             : 
    1428             : /** Rename the group.
    1429             :  *
    1430             :  * This is not implemented by all drivers.
    1431             :  *
    1432             :  * Drivers known to implement it: MEM, netCDF, ZARR.
    1433             :  *
    1434             :  * This is the same as the C function GDALGroupRename().
    1435             :  *
    1436             :  * @param osNewName New name.
    1437             :  *
    1438             :  * @return true in case of success
    1439             :  * @since GDAL 3.8
    1440             :  */
    1441           0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
    1442             : {
    1443           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
    1444           0 :     return false;
    1445             : }
    1446             : 
    1447             : /************************************************************************/
    1448             : /*                         BaseRename()                                 */
    1449             : /************************************************************************/
    1450             : 
    1451             : //! @cond Doxygen_Suppress
    1452           8 : void GDALGroup::BaseRename(const std::string &osNewName)
    1453             : {
    1454           8 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
    1455           8 :     m_osFullName += osNewName;
    1456           8 :     m_osName = osNewName;
    1457             : 
    1458           8 :     NotifyChildrenOfRenaming();
    1459           8 : }
    1460             : 
    1461             : //! @endcond
    1462             : 
    1463             : /************************************************************************/
    1464             : /*                        ParentRenamed()                               */
    1465             : /************************************************************************/
    1466             : 
    1467             : //! @cond Doxygen_Suppress
    1468           7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
    1469             : {
    1470           7 :     m_osFullName = osNewParentFullName;
    1471           7 :     m_osFullName += "/";
    1472           7 :     m_osFullName += m_osName;
    1473             : 
    1474           7 :     NotifyChildrenOfRenaming();
    1475           7 : }
    1476             : 
    1477             : //! @endcond
    1478             : 
    1479             : /************************************************************************/
    1480             : /*                             Deleted()                                */
    1481             : /************************************************************************/
    1482             : 
    1483             : //! @cond Doxygen_Suppress
    1484          22 : void GDALGroup::Deleted()
    1485             : {
    1486          22 :     m_bValid = false;
    1487             : 
    1488          22 :     NotifyChildrenOfDeletion();
    1489          22 : }
    1490             : 
    1491             : //! @endcond
    1492             : 
    1493             : /************************************************************************/
    1494             : /*                        ParentDeleted()                               */
    1495             : /************************************************************************/
    1496             : 
    1497             : //! @cond Doxygen_Suppress
    1498           3 : void GDALGroup::ParentDeleted()
    1499             : {
    1500           3 :     Deleted();
    1501           3 : }
    1502             : 
    1503             : //! @endcond
    1504             : 
    1505             : /************************************************************************/
    1506             : /*                     CheckValidAndErrorOutIfNot()                     */
    1507             : /************************************************************************/
    1508             : 
    1509             : //! @cond Doxygen_Suppress
    1510       11819 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
    1511             : {
    1512       11819 :     if (!m_bValid)
    1513             :     {
    1514          14 :         CPLError(CE_Failure, CPLE_AppDefined,
    1515             :                  "This object has been deleted. No action on it is possible");
    1516             :     }
    1517       11819 :     return m_bValid;
    1518             : }
    1519             : 
    1520             : //! @endcond
    1521             : 
    1522             : /************************************************************************/
    1523             : /*                       ~GDALAbstractMDArray()                         */
    1524             : /************************************************************************/
    1525             : 
    1526             : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
    1527             : 
    1528             : /************************************************************************/
    1529             : /*                        GDALAbstractMDArray()                         */
    1530             : /************************************************************************/
    1531             : 
    1532             : //! @cond Doxygen_Suppress
    1533       20211 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
    1534       20211 :                                          const std::string &osName)
    1535             :     : m_osName(osName),
    1536             :       m_osFullName(
    1537       20211 :           !osParentName.empty()
    1538       38783 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
    1539       79205 :               : osName)
    1540             : {
    1541       20211 : }
    1542             : 
    1543             : //! @endcond
    1544             : 
    1545             : /************************************************************************/
    1546             : /*                           GetDimensions()                            */
    1547             : /************************************************************************/
    1548             : 
    1549             : /** \fn GDALAbstractMDArray::GetDimensions() const
    1550             :  * \brief Return the dimensions of an attribute/array.
    1551             :  *
    1552             :  * This is the same as the C functions GDALMDArrayGetDimensions() and
    1553             :  * similar to GDALAttributeGetDimensionsSize().
    1554             :  */
    1555             : 
    1556             : /************************************************************************/
    1557             : /*                           GetDataType()                              */
    1558             : /************************************************************************/
    1559             : 
    1560             : /** \fn GDALAbstractMDArray::GetDataType() const
    1561             :  * \brief Return the data type of an attribute/array.
    1562             :  *
    1563             :  * This is the same as the C functions GDALMDArrayGetDataType() and
    1564             :  * GDALAttributeGetDataType()
    1565             :  */
    1566             : 
    1567             : /************************************************************************/
    1568             : /*                        GetDimensionCount()                           */
    1569             : /************************************************************************/
    1570             : 
    1571             : /** Return the number of dimensions.
    1572             :  *
    1573             :  * Default implementation is GetDimensions().size(), and may be overridden by
    1574             :  * drivers if they have a faster / less expensive implementations.
    1575             :  *
    1576             :  * This is the same as the C function GDALMDArrayGetDimensionCount() or
    1577             :  * GDALAttributeGetDimensionCount().
    1578             :  *
    1579             :  */
    1580       22383 : size_t GDALAbstractMDArray::GetDimensionCount() const
    1581             : {
    1582       22383 :     return GetDimensions().size();
    1583             : }
    1584             : 
    1585             : /************************************************************************/
    1586             : /*                            Rename()                                  */
    1587             : /************************************************************************/
    1588             : 
    1589             : /** Rename the attribute/array.
    1590             :  *
    1591             :  * This is not implemented by all drivers.
    1592             :  *
    1593             :  * Drivers known to implement it: MEM, netCDF, Zarr.
    1594             :  *
    1595             :  * This is the same as the C functions GDALMDArrayRename() or
    1596             :  * GDALAttributeRename().
    1597             :  *
    1598             :  * @param osNewName New name.
    1599             :  *
    1600             :  * @return true in case of success
    1601             :  * @since GDAL 3.8
    1602             :  */
    1603           0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
    1604             : {
    1605           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
    1606           0 :     return false;
    1607             : }
    1608             : 
    1609             : /************************************************************************/
    1610             : /*                             CopyValue()                              */
    1611             : /************************************************************************/
    1612             : 
    1613             : /** Convert a value from a source type to a destination type.
    1614             :  *
    1615             :  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
    1616             :  * that must be freed with CPLFree().
    1617             :  */
    1618       78829 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
    1619             :                                      const GDALExtendedDataType &srcType,
    1620             :                                      void *pDst,
    1621             :                                      const GDALExtendedDataType &dstType)
    1622             : {
    1623      154365 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1624       75536 :         dstType.GetClass() == GEDTC_NUMERIC)
    1625             :     {
    1626       75329 :         GDALCopyWords64(pSrc, srcType.GetNumericDataType(), 0, pDst,
    1627             :                         dstType.GetNumericDataType(), 0, 1);
    1628       75329 :         return true;
    1629             :     }
    1630        6618 :     if (srcType.GetClass() == GEDTC_STRING &&
    1631        3118 :         dstType.GetClass() == GEDTC_STRING)
    1632             :     {
    1633             :         const char *srcStrPtr;
    1634        2733 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
    1635        2733 :         char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
    1636        2733 :         *reinterpret_cast<void **>(pDst) = pszDup;
    1637        2733 :         return true;
    1638             :     }
    1639         974 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1640         207 :         dstType.GetClass() == GEDTC_STRING)
    1641             :     {
    1642         207 :         const char *str = nullptr;
    1643         207 :         switch (srcType.GetNumericDataType())
    1644             :         {
    1645           0 :             case GDT_Unknown:
    1646           0 :                 break;
    1647           0 :             case GDT_Byte:
    1648           0 :                 str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
    1649           0 :                 break;
    1650           3 :             case GDT_Int8:
    1651           3 :                 str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
    1652           3 :                 break;
    1653          48 :             case GDT_UInt16:
    1654          48 :                 str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
    1655          48 :                 break;
    1656           0 :             case GDT_Int16:
    1657           0 :                 str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
    1658           0 :                 break;
    1659           8 :             case GDT_UInt32:
    1660           8 :                 str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
    1661           8 :                 break;
    1662          54 :             case GDT_Int32:
    1663          54 :                 str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
    1664          54 :                 break;
    1665           0 :             case GDT_UInt64:
    1666             :                 str =
    1667           0 :                     CPLSPrintf(CPL_FRMT_GUIB,
    1668             :                                static_cast<GUIntBig>(
    1669             :                                    *static_cast<const std::uint64_t *>(pSrc)));
    1670           0 :                 break;
    1671           0 :             case GDT_Int64:
    1672           0 :                 str = CPLSPrintf(CPL_FRMT_GIB,
    1673             :                                  static_cast<GIntBig>(
    1674             :                                      *static_cast<const std::int64_t *>(pSrc)));
    1675           0 :                 break;
    1676          17 :             case GDT_Float32:
    1677          17 :                 str = CPLSPrintf("%.9g", *static_cast<const float *>(pSrc));
    1678          17 :                 break;
    1679          75 :             case GDT_Float64:
    1680          75 :                 str = CPLSPrintf("%.17g", *static_cast<const double *>(pSrc));
    1681          75 :                 break;
    1682           2 :             case GDT_CInt16:
    1683             :             {
    1684           2 :                 const GInt16 *src = static_cast<const GInt16 *>(pSrc);
    1685           2 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
    1686           2 :                 break;
    1687             :             }
    1688           0 :             case GDT_CInt32:
    1689             :             {
    1690           0 :                 const GInt32 *src = static_cast<const GInt32 *>(pSrc);
    1691           0 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
    1692           0 :                 break;
    1693             :             }
    1694           0 :             case GDT_CFloat32:
    1695             :             {
    1696           0 :                 const float *src = static_cast<const float *>(pSrc);
    1697           0 :                 str = CPLSPrintf("%.9g+%.9gj", src[0], src[1]);
    1698           0 :                 break;
    1699             :             }
    1700           0 :             case GDT_CFloat64:
    1701             :             {
    1702           0 :                 const double *src = static_cast<const double *>(pSrc);
    1703           0 :                 str = CPLSPrintf("%.17g+%.17gj", src[0], src[1]);
    1704           0 :                 break;
    1705             :             }
    1706           0 :             case GDT_TypeCount:
    1707           0 :                 CPLAssert(false);
    1708             :                 break;
    1709             :         }
    1710         207 :         char *pszDup = str ? CPLStrdup(str) : nullptr;
    1711         207 :         *reinterpret_cast<void **>(pDst) = pszDup;
    1712         207 :         return true;
    1713             :     }
    1714         945 :     if (srcType.GetClass() == GEDTC_STRING &&
    1715         385 :         dstType.GetClass() == GEDTC_NUMERIC)
    1716             :     {
    1717             :         const char *srcStrPtr;
    1718         385 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
    1719         385 :         if (dstType.GetNumericDataType() == GDT_Int64)
    1720             :         {
    1721           2 :             *(static_cast<int64_t *>(pDst)) =
    1722           2 :                 srcStrPtr == nullptr ? 0
    1723           1 :                                      : static_cast<int64_t>(atoll(srcStrPtr));
    1724             :         }
    1725         383 :         else if (dstType.GetNumericDataType() == GDT_UInt64)
    1726             :         {
    1727           2 :             *(static_cast<uint64_t *>(pDst)) =
    1728           2 :                 srcStrPtr == nullptr
    1729           2 :                     ? 0
    1730           1 :                     : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
    1731             :         }
    1732             :         else
    1733             :         {
    1734             :             // FIXME GDT_UInt64
    1735         381 :             const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
    1736         381 :             GDALCopyWords64(&dfVal, GDT_Float64, 0, pDst,
    1737             :                             dstType.GetNumericDataType(), 0, 1);
    1738             :         }
    1739         385 :         return true;
    1740             :     }
    1741         350 :     if (srcType.GetClass() == GEDTC_COMPOUND &&
    1742         175 :         dstType.GetClass() == GEDTC_COMPOUND)
    1743             :     {
    1744         175 :         const auto &srcComponents = srcType.GetComponents();
    1745         175 :         const auto &dstComponents = dstType.GetComponents();
    1746         175 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
    1747         175 :         GByte *pabyDst = static_cast<GByte *>(pDst);
    1748             : 
    1749             :         std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
    1750         350 :             srcComponentMap;
    1751         688 :         for (const auto &srcComp : srcComponents)
    1752             :         {
    1753         513 :             srcComponentMap[srcComp->GetName()] = &srcComp;
    1754             :         }
    1755         504 :         for (const auto &dstComp : dstComponents)
    1756             :         {
    1757         329 :             auto oIter = srcComponentMap.find(dstComp->GetName());
    1758         329 :             if (oIter == srcComponentMap.end())
    1759           0 :                 return false;
    1760         329 :             const auto &srcComp = *(oIter->second);
    1761         987 :             if (!GDALExtendedDataType::CopyValue(
    1762         329 :                     pabySrc + srcComp->GetOffset(), srcComp->GetType(),
    1763         329 :                     pabyDst + dstComp->GetOffset(), dstComp->GetType()))
    1764             :             {
    1765           0 :                 return false;
    1766             :             }
    1767             :         }
    1768         175 :         return true;
    1769             :     }
    1770             : 
    1771           0 :     return false;
    1772             : }
    1773             : 
    1774             : /************************************************************************/
    1775             : /*                             CopyValues()                             */
    1776             : /************************************************************************/
    1777             : 
    1778             : /** Convert severals value from a source type to a destination type.
    1779             :  *
    1780             :  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
    1781             :  * that must be freed with CPLFree().
    1782             :  */
    1783         328 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
    1784             :                                       const GDALExtendedDataType &srcType,
    1785             :                                       GPtrDiff_t nSrcStrideInElts, void *pDst,
    1786             :                                       const GDALExtendedDataType &dstType,
    1787             :                                       GPtrDiff_t nDstStrideInElts,
    1788             :                                       size_t nValues)
    1789             : {
    1790             :     const auto nSrcStrideInBytes =
    1791         328 :         nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
    1792             :     const auto nDstStrideInBytes =
    1793         328 :         nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
    1794         594 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1795         266 :         dstType.GetClass() == GEDTC_NUMERIC &&
    1796         266 :         nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
    1797         266 :         nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
    1798         860 :         nDstStrideInBytes >= std::numeric_limits<int>::min() &&
    1799         266 :         nDstStrideInBytes <= std::numeric_limits<int>::max())
    1800             :     {
    1801         266 :         GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
    1802             :                         static_cast<int>(nSrcStrideInBytes), pDst,
    1803             :                         dstType.GetNumericDataType(),
    1804             :                         static_cast<int>(nDstStrideInBytes), nValues);
    1805             :     }
    1806             :     else
    1807             :     {
    1808          62 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
    1809          62 :         GByte *pabyDst = static_cast<GByte *>(pDst);
    1810         124 :         for (size_t i = 0; i < nValues; ++i)
    1811             :         {
    1812          62 :             if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
    1813           0 :                 return false;
    1814          62 :             pabySrc += nSrcStrideInBytes;
    1815          62 :             pabyDst += nDstStrideInBytes;
    1816             :         }
    1817             :     }
    1818         328 :     return true;
    1819             : }
    1820             : 
    1821             : /************************************************************************/
    1822             : /*                       CheckReadWriteParams()                         */
    1823             : /************************************************************************/
    1824             : //! @cond Doxygen_Suppress
    1825        7869 : bool GDALAbstractMDArray::CheckReadWriteParams(
    1826             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
    1827             :     const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
    1828             :     const void *buffer, const void *buffer_alloc_start,
    1829             :     size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
    1830             :     std::vector<GPtrDiff_t> &tmp_bufferStride) const
    1831             : {
    1832           0 :     const auto lamda_error = []()
    1833             :     {
    1834           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1835             :                  "Not all elements pointed by buffer will fit in "
    1836             :                  "[buffer_alloc_start, "
    1837             :                  "buffer_alloc_start + buffer_alloc_size]");
    1838           0 :     };
    1839             : 
    1840        7869 :     const auto &dims = GetDimensions();
    1841        7869 :     if (dims.empty())
    1842             :     {
    1843        3034 :         if (buffer_alloc_start)
    1844             :         {
    1845        2667 :             const size_t elementSize = bufferDataType.GetSize();
    1846        2667 :             const GByte *paby_buffer = static_cast<const GByte *>(buffer);
    1847        2667 :             const GByte *paby_buffer_alloc_start =
    1848             :                 static_cast<const GByte *>(buffer_alloc_start);
    1849        2667 :             const GByte *paby_buffer_alloc_end =
    1850             :                 paby_buffer_alloc_start + buffer_alloc_size;
    1851             : 
    1852        2667 :             if (paby_buffer < paby_buffer_alloc_start ||
    1853        2667 :                 paby_buffer + elementSize > paby_buffer_alloc_end)
    1854             :             {
    1855           0 :                 lamda_error();
    1856           0 :                 return false;
    1857             :             }
    1858             :         }
    1859        3034 :         return true;
    1860             :     }
    1861             : 
    1862        4835 :     if (arrayStep == nullptr)
    1863             :     {
    1864        1270 :         tmp_arrayStep.resize(dims.size(), 1);
    1865        1270 :         arrayStep = tmp_arrayStep.data();
    1866             :     }
    1867       13691 :     for (size_t i = 0; i < dims.size(); i++)
    1868             :     {
    1869        8856 :         if (count[i] == 0)
    1870             :         {
    1871           0 :             CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
    1872             :                      static_cast<unsigned>(i));
    1873           0 :             return false;
    1874             :         }
    1875             :     }
    1876        4835 :     bool bufferStride_all_positive = true;
    1877        4835 :     if (bufferStride == nullptr)
    1878             :     {
    1879         983 :         GPtrDiff_t stride = 1;
    1880             :         // To compute strides we must proceed from the fastest varying dimension
    1881             :         // (the last one), and then reverse the result
    1882        2237 :         for (size_t i = dims.size(); i != 0;)
    1883             :         {
    1884        1254 :             --i;
    1885        1254 :             tmp_bufferStride.push_back(stride);
    1886        1254 :             GUInt64 newStride = 0;
    1887             :             bool bOK;
    1888             :             try
    1889             :             {
    1890        1254 :                 newStride = (CPLSM(static_cast<uint64_t>(stride)) *
    1891        2508 :                              CPLSM(static_cast<uint64_t>(count[i])))
    1892        1254 :                                 .v();
    1893        1254 :                 bOK = static_cast<size_t>(newStride) == newStride &&
    1894        1254 :                       newStride < std::numeric_limits<size_t>::max() / 2;
    1895             :             }
    1896           0 :             catch (...)
    1897             :             {
    1898           0 :                 bOK = false;
    1899             :             }
    1900        1254 :             if (!bOK)
    1901             :             {
    1902           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
    1903           0 :                 return false;
    1904             :             }
    1905        1254 :             stride = static_cast<GPtrDiff_t>(newStride);
    1906             :         }
    1907         983 :         std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
    1908         983 :         bufferStride = tmp_bufferStride.data();
    1909             :     }
    1910             :     else
    1911             :     {
    1912       11452 :         for (size_t i = 0; i < dims.size(); i++)
    1913             :         {
    1914        7601 :             if (bufferStride[i] < 0)
    1915             :             {
    1916           1 :                 bufferStride_all_positive = false;
    1917           1 :                 break;
    1918             :             }
    1919             :         }
    1920             :     }
    1921       13662 :     for (size_t i = 0; i < dims.size(); i++)
    1922             :     {
    1923        8837 :         if (arrayStartIdx[i] >= dims[i]->GetSize())
    1924             :         {
    1925           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    1926             :                      "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
    1927             :                      static_cast<unsigned>(i),
    1928           2 :                      static_cast<GUInt64>(arrayStartIdx[i]),
    1929           2 :                      static_cast<GUInt64>(dims[i]->GetSize()));
    1930           2 :             return false;
    1931             :         }
    1932             :         bool bOverflow;
    1933        8835 :         if (arrayStep[i] >= 0)
    1934             :         {
    1935             :             try
    1936             :             {
    1937        8241 :                 bOverflow = (CPLSM(static_cast<uint64_t>(arrayStartIdx[i])) +
    1938        8243 :                              CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    1939       32967 :                                  CPLSM(static_cast<uint64_t>(arrayStep[i])))
    1940        8241 :                                 .v() >= dims[i]->GetSize();
    1941             :             }
    1942           1 :             catch (...)
    1943             :             {
    1944           1 :                 bOverflow = true;
    1945             :             }
    1946        8242 :             if (bOverflow)
    1947             :             {
    1948           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1949             :                          "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
    1950             :                          ">= " CPL_FRMT_GUIB,
    1951             :                          static_cast<unsigned>(i), static_cast<unsigned>(i),
    1952             :                          static_cast<unsigned>(i),
    1953           5 :                          static_cast<GUInt64>(dims[i]->GetSize()));
    1954           5 :                 return false;
    1955             :             }
    1956             :         }
    1957             :         else
    1958             :         {
    1959             :             try
    1960             :             {
    1961         593 :                 bOverflow =
    1962         593 :                     arrayStartIdx[i] <
    1963         593 :                     (CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    1964        1186 :                      CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
    1965             :                                ? (static_cast<uint64_t>(1) << 63)
    1966        1186 :                                : static_cast<uint64_t>(-arrayStep[i])))
    1967         593 :                         .v();
    1968             :             }
    1969           0 :             catch (...)
    1970             :             {
    1971           0 :                 bOverflow = true;
    1972             :             }
    1973         593 :             if (bOverflow)
    1974             :             {
    1975           3 :                 CPLError(
    1976             :                     CE_Failure, CPLE_AppDefined,
    1977             :                     "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
    1978             :                     static_cast<unsigned>(i), static_cast<unsigned>(i),
    1979             :                     static_cast<unsigned>(i));
    1980           3 :                 return false;
    1981             :             }
    1982             :         }
    1983             :     }
    1984             : 
    1985        4825 :     if (buffer_alloc_start)
    1986             :     {
    1987        2524 :         const size_t elementSize = bufferDataType.GetSize();
    1988        2524 :         const GByte *paby_buffer = static_cast<const GByte *>(buffer);
    1989        2524 :         const GByte *paby_buffer_alloc_start =
    1990             :             static_cast<const GByte *>(buffer_alloc_start);
    1991        2524 :         const GByte *paby_buffer_alloc_end =
    1992             :             paby_buffer_alloc_start + buffer_alloc_size;
    1993        2524 :         if (bufferStride_all_positive)
    1994             :         {
    1995        2524 :             if (paby_buffer < paby_buffer_alloc_start)
    1996             :             {
    1997           0 :                 lamda_error();
    1998           0 :                 return false;
    1999             :             }
    2000        2524 :             GUInt64 nOffset = elementSize;
    2001        7289 :             for (size_t i = 0; i < dims.size(); i++)
    2002             :             {
    2003             :                 try
    2004             :                 {
    2005        4765 :                     nOffset = (CPLSM(static_cast<uint64_t>(nOffset)) +
    2006        4765 :                                CPLSM(static_cast<uint64_t>(bufferStride[i])) *
    2007        9530 :                                    CPLSM(static_cast<uint64_t>(count[i] - 1)) *
    2008       19060 :                                    CPLSM(static_cast<uint64_t>(elementSize)))
    2009        4765 :                                   .v();
    2010             :                 }
    2011           0 :                 catch (...)
    2012             :                 {
    2013           0 :                     lamda_error();
    2014           0 :                     return false;
    2015             :                 }
    2016             :             }
    2017             : #if SIZEOF_VOIDP == 4
    2018             :             if (static_cast<size_t>(nOffset) != nOffset)
    2019             :             {
    2020             :                 lamda_error();
    2021             :                 return false;
    2022             :             }
    2023             : #endif
    2024        2524 :             if (paby_buffer + nOffset > paby_buffer_alloc_end)
    2025             :             {
    2026           0 :                 lamda_error();
    2027           0 :                 return false;
    2028             :             }
    2029             :         }
    2030           0 :         else if (dims.size() < 31)
    2031             :         {
    2032             :             // Check all corners of the hypercube
    2033           0 :             const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
    2034           0 :             for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
    2035             :             {
    2036           0 :                 const GByte *paby = paby_buffer;
    2037           0 :                 for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
    2038             :                      i++)
    2039             :                 {
    2040           0 :                     if (iCornerCode & (1U << i))
    2041             :                     {
    2042             :                         // We should check for integer overflows
    2043           0 :                         paby += bufferStride[i] * (count[i] - 1) * elementSize;
    2044             :                     }
    2045             :                 }
    2046           0 :                 if (paby < paby_buffer_alloc_start ||
    2047           0 :                     paby + elementSize > paby_buffer_alloc_end)
    2048             :                 {
    2049           0 :                     lamda_error();
    2050           0 :                     return false;
    2051             :                 }
    2052             :             }
    2053             :         }
    2054             :     }
    2055             : 
    2056        4825 :     return true;
    2057             : }
    2058             : 
    2059             : //! @endcond
    2060             : 
    2061             : /************************************************************************/
    2062             : /*                               Read()                                 */
    2063             : /************************************************************************/
    2064             : 
    2065             : /** Read part or totality of a multidimensional array or attribute.
    2066             :  *
    2067             :  * This will extract the content of a hyper-rectangle from the array into
    2068             :  * a user supplied buffer.
    2069             :  *
    2070             :  * If bufferDataType is of type string, the values written in pDstBuffer
    2071             :  * will be char* pointers and the strings should be freed with CPLFree().
    2072             :  *
    2073             :  * This is the same as the C function GDALMDArrayRead().
    2074             :  *
    2075             :  * @param arrayStartIdx Values representing the starting index to read
    2076             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2077             :  *                      Array of GetDimensionCount() values. Must not be
    2078             :  *                      nullptr, unless for a zero-dimensional array.
    2079             :  *
    2080             :  * @param count         Values representing the number of values to extract in
    2081             :  *                      each dimension.
    2082             :  *                      Array of GetDimensionCount() values. Must not be
    2083             :  *                      nullptr, unless for a zero-dimensional array.
    2084             :  *
    2085             :  * @param arrayStep     Spacing between values to extract in each dimension.
    2086             :  *                      The spacing is in number of array elements, not bytes.
    2087             :  *                      If provided, must contain GetDimensionCount() values.
    2088             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
    2089             :  * default to indicate consecutive elements.
    2090             :  *
    2091             :  * @param bufferStride  Spacing between values to store in pDstBuffer.
    2092             :  *                      The spacing is in number of array elements, not bytes.
    2093             :  *                      If provided, must contain GetDimensionCount() values.
    2094             :  *                      Negative values are possible (for example to reorder
    2095             :  *                      from bottom-to-top to top-to-bottom).
    2096             :  *                      If set to nullptr, will be set so that pDstBuffer is
    2097             :  *                      written in a compact way, with elements of the last /
    2098             :  *                      fastest varying dimension being consecutive.
    2099             :  *
    2100             :  * @param bufferDataType Data type of values in pDstBuffer.
    2101             :  *
    2102             :  * @param pDstBuffer    User buffer to store the values read. Should be big
    2103             :  *                      enough to store the number of values indicated by
    2104             :  * count[] and with the spacing of bufferStride[].
    2105             :  *
    2106             :  * @param pDstBufferAllocStart Optional pointer that can be used to validate the
    2107             :  *                             validity of pDstBuffer. pDstBufferAllocStart
    2108             :  * should be the pointer returned by the malloc() or equivalent call used to
    2109             :  * allocate the buffer. It will generally be equal to pDstBuffer (when
    2110             :  * bufferStride[] values are all positive), but not necessarily. If specified,
    2111             :  * nDstBufferAllocSize should be also set to the appropriate value. If no
    2112             :  * validation is needed, nullptr can be passed.
    2113             :  *
    2114             :  * @param nDstBufferAllocSize  Optional buffer size, that can be used to
    2115             :  * validate the validity of pDstBuffer. This is the size of the buffer starting
    2116             :  * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
    2117             :  *                             set to the appropriate value.
    2118             :  *                             If no validation is needed, 0 can be passed.
    2119             :  *
    2120             :  * @return true in case of success.
    2121             :  */
    2122        2344 : bool GDALAbstractMDArray::Read(
    2123             :     const GUInt64 *arrayStartIdx, const size_t *count,
    2124             :     const GInt64 *arrayStep,         // step in elements
    2125             :     const GPtrDiff_t *bufferStride,  // stride in elements
    2126             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    2127             :     const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
    2128             : {
    2129        2344 :     if (!GetDataType().CanConvertTo(bufferDataType))
    2130             :     {
    2131           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2132             :                  "Array data type is not convertible to buffer data type");
    2133           0 :         return false;
    2134             :     }
    2135             : 
    2136        4688 :     std::vector<GInt64> tmp_arrayStep;
    2137        4688 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    2138        2344 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    2139             :                               bufferDataType, pDstBuffer, pDstBufferAllocStart,
    2140             :                               nDstBufferAllocSize, tmp_arrayStep,
    2141             :                               tmp_bufferStride))
    2142             :     {
    2143           0 :         return false;
    2144             :     }
    2145             : 
    2146        2344 :     return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
    2147        2344 :                  pDstBuffer);
    2148             : }
    2149             : 
    2150             : /************************************************************************/
    2151             : /*                                IWrite()                              */
    2152             : /************************************************************************/
    2153             : 
    2154             : //! @cond Doxygen_Suppress
    2155           1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
    2156             :                                  const GInt64 *, const GPtrDiff_t *,
    2157             :                                  const GDALExtendedDataType &, const void *)
    2158             : {
    2159           1 :     CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
    2160           1 :     return false;
    2161             : }
    2162             : 
    2163             : //! @endcond
    2164             : 
    2165             : /************************************************************************/
    2166             : /*                               Write()                                 */
    2167             : /************************************************************************/
    2168             : 
    2169             : /** Write part or totality of a multidimensional array or attribute.
    2170             :  *
    2171             :  * This will set the content of a hyper-rectangle into the array from
    2172             :  * a user supplied buffer.
    2173             :  *
    2174             :  * If bufferDataType is of type string, the values read from pSrcBuffer
    2175             :  * will be char* pointers.
    2176             :  *
    2177             :  * This is the same as the C function GDALMDArrayWrite().
    2178             :  *
    2179             :  * @param arrayStartIdx Values representing the starting index to write
    2180             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2181             :  *                      Array of GetDimensionCount() values. Must not be
    2182             :  *                      nullptr, unless for a zero-dimensional array.
    2183             :  *
    2184             :  * @param count         Values representing the number of values to write in
    2185             :  *                      each dimension.
    2186             :  *                      Array of GetDimensionCount() values. Must not be
    2187             :  *                      nullptr, unless for a zero-dimensional array.
    2188             :  *
    2189             :  * @param arrayStep     Spacing between values to write in each dimension.
    2190             :  *                      The spacing is in number of array elements, not bytes.
    2191             :  *                      If provided, must contain GetDimensionCount() values.
    2192             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
    2193             :  * default to indicate consecutive elements.
    2194             :  *
    2195             :  * @param bufferStride  Spacing between values to read from pSrcBuffer.
    2196             :  *                      The spacing is in number of array elements, not bytes.
    2197             :  *                      If provided, must contain GetDimensionCount() values.
    2198             :  *                      Negative values are possible (for example to reorder
    2199             :  *                      from bottom-to-top to top-to-bottom).
    2200             :  *                      If set to nullptr, will be set so that pSrcBuffer is
    2201             :  *                      written in a compact way, with elements of the last /
    2202             :  *                      fastest varying dimension being consecutive.
    2203             :  *
    2204             :  * @param bufferDataType Data type of values in pSrcBuffer.
    2205             :  *
    2206             :  * @param pSrcBuffer    User buffer to read the values from. Should be big
    2207             :  *                      enough to store the number of values indicated by
    2208             :  * count[] and with the spacing of bufferStride[].
    2209             :  *
    2210             :  * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
    2211             :  *                             validity of pSrcBuffer. pSrcBufferAllocStart
    2212             :  * should be the pointer returned by the malloc() or equivalent call used to
    2213             :  * allocate the buffer. It will generally be equal to pSrcBuffer (when
    2214             :  * bufferStride[] values are all positive), but not necessarily. If specified,
    2215             :  * nSrcBufferAllocSize should be also set to the appropriate value. If no
    2216             :  * validation is needed, nullptr can be passed.
    2217             :  *
    2218             :  * @param nSrcBufferAllocSize  Optional buffer size, that can be used to
    2219             :  * validate the validity of pSrcBuffer. This is the size of the buffer starting
    2220             :  * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
    2221             :  *                             set to the appropriate value.
    2222             :  *                             If no validation is needed, 0 can be passed.
    2223             :  *
    2224             :  * @return true in case of success.
    2225             :  */
    2226        1774 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
    2227             :                                 const size_t *count, const GInt64 *arrayStep,
    2228             :                                 const GPtrDiff_t *bufferStride,
    2229             :                                 const GDALExtendedDataType &bufferDataType,
    2230             :                                 const void *pSrcBuffer,
    2231             :                                 const void *pSrcBufferAllocStart,
    2232             :                                 size_t nSrcBufferAllocSize)
    2233             : {
    2234        1774 :     if (!bufferDataType.CanConvertTo(GetDataType()))
    2235             :     {
    2236           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2237             :                  "Buffer data type is not convertible to array data type");
    2238           0 :         return false;
    2239             :     }
    2240             : 
    2241        3548 :     std::vector<GInt64> tmp_arrayStep;
    2242        3548 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    2243        1774 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    2244             :                               bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
    2245             :                               nSrcBufferAllocSize, tmp_arrayStep,
    2246             :                               tmp_bufferStride))
    2247             :     {
    2248           0 :         return false;
    2249             :     }
    2250             : 
    2251        1774 :     return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
    2252        1774 :                   pSrcBuffer);
    2253             : }
    2254             : 
    2255             : /************************************************************************/
    2256             : /*                          GetTotalElementsCount()                     */
    2257             : /************************************************************************/
    2258             : 
    2259             : /** Return the total number of values in the array.
    2260             :  *
    2261             :  * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
    2262             :  * and GDALAttributeGetTotalElementsCount().
    2263             :  *
    2264             :  */
    2265        1022 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
    2266             : {
    2267        1022 :     const auto &dims = GetDimensions();
    2268        1022 :     if (dims.empty())
    2269         504 :         return 1;
    2270         518 :     GUInt64 nElts = 1;
    2271        1146 :     for (const auto &dim : dims)
    2272             :     {
    2273             :         try
    2274             :         {
    2275         628 :             nElts = (CPLSM(static_cast<uint64_t>(nElts)) *
    2276        1884 :                      CPLSM(static_cast<uint64_t>(dim->GetSize())))
    2277         628 :                         .v();
    2278             :         }
    2279           0 :         catch (...)
    2280             :         {
    2281           0 :             return 0;
    2282             :         }
    2283             :     }
    2284         518 :     return nElts;
    2285             : }
    2286             : 
    2287             : /************************************************************************/
    2288             : /*                           GetBlockSize()                             */
    2289             : /************************************************************************/
    2290             : 
    2291             : /** Return the "natural" block size of the array along all dimensions.
    2292             :  *
    2293             :  * Some drivers might organize the array in tiles/blocks and reading/writing
    2294             :  * aligned on those tile/block boundaries will be more efficient.
    2295             :  *
    2296             :  * The returned number of elements in the vector is the same as
    2297             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
    2298             :  * the natural block size along the considered dimension.
    2299             :  * "Flat" arrays will typically return a vector of values set to 0.
    2300             :  *
    2301             :  * The default implementation will return a vector of values set to 0.
    2302             :  *
    2303             :  * This method is used by GetProcessingChunkSize().
    2304             :  *
    2305             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
    2306             :  * theoretical case of a 32-bit platform, this might exceed its size_t
    2307             :  * allocation capabilities.
    2308             :  *
    2309             :  * This is the same as the C function GDALMDArrayGetBlockSize().
    2310             :  *
    2311             :  * @return the block size, in number of elements along each dimension.
    2312             :  */
    2313         221 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
    2314             : {
    2315         221 :     return std::vector<GUInt64>(GetDimensionCount());
    2316             : }
    2317             : 
    2318             : /************************************************************************/
    2319             : /*                       GetProcessingChunkSize()                       */
    2320             : /************************************************************************/
    2321             : 
    2322             : /** \brief Return an optimal chunk size for read/write operations, given the
    2323             :  * natural block size and memory constraints specified.
    2324             :  *
    2325             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
    2326             :  * multiple of those returned by GetBlockSize() (unless the block define by
    2327             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
    2328             :  * returned by this method).
    2329             :  *
    2330             :  * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
    2331             :  *
    2332             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
    2333             :  * chunk.
    2334             :  *
    2335             :  * @return the chunk size, in number of elements along each dimension.
    2336             :  */
    2337             : std::vector<size_t>
    2338          60 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
    2339             : {
    2340          60 :     const auto &dims = GetDimensions();
    2341          60 :     const auto &nDTSize = GetDataType().GetSize();
    2342          60 :     std::vector<size_t> anChunkSize;
    2343         120 :     auto blockSize = GetBlockSize();
    2344          60 :     CPLAssert(blockSize.size() == dims.size());
    2345          60 :     size_t nChunkSize = nDTSize;
    2346          60 :     bool bOverflow = false;
    2347          60 :     constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
    2348             :     // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
    2349             :     // [1, min(sizet_max, dim_size[i])]
    2350             :     // Also make sure that the product of all anChunkSize[i]) fits on size_t
    2351         173 :     for (size_t i = 0; i < dims.size(); i++)
    2352             :     {
    2353             :         const auto sizeDimI =
    2354         226 :             std::max(static_cast<size_t>(1),
    2355         226 :                      static_cast<size_t>(
    2356         226 :                          std::min(static_cast<GUInt64>(kSIZE_T_MAX),
    2357         113 :                                   std::min(blockSize[i], dims[i]->GetSize()))));
    2358         113 :         anChunkSize.push_back(sizeDimI);
    2359         113 :         if (nChunkSize > kSIZE_T_MAX / sizeDimI)
    2360             :         {
    2361           4 :             bOverflow = true;
    2362             :         }
    2363             :         else
    2364             :         {
    2365         109 :             nChunkSize *= sizeDimI;
    2366             :         }
    2367             :     }
    2368          60 :     if (nChunkSize == 0)
    2369           0 :         return anChunkSize;
    2370             : 
    2371             :     // If the product of all anChunkSize[i] does not fit on size_t, then
    2372             :     // set lowest anChunkSize[i] to 1.
    2373          60 :     if (bOverflow)
    2374             :     {
    2375           2 :         nChunkSize = nDTSize;
    2376           2 :         bOverflow = false;
    2377           8 :         for (size_t i = dims.size(); i > 0;)
    2378             :         {
    2379           6 :             --i;
    2380           6 :             if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
    2381             :             {
    2382           4 :                 bOverflow = true;
    2383           4 :                 anChunkSize[i] = 1;
    2384             :             }
    2385             :             else
    2386             :             {
    2387           2 :                 nChunkSize *= anChunkSize[i];
    2388             :             }
    2389             :         }
    2390             :     }
    2391             : 
    2392          60 :     nChunkSize = nDTSize;
    2393         120 :     std::vector<size_t> anAccBlockSizeFromStart;
    2394         173 :     for (size_t i = 0; i < dims.size(); i++)
    2395             :     {
    2396         113 :         nChunkSize *= anChunkSize[i];
    2397         113 :         anAccBlockSizeFromStart.push_back(nChunkSize);
    2398             :     }
    2399          60 :     if (nChunkSize <= nMaxChunkMemory / 2)
    2400             :     {
    2401          56 :         size_t nVoxelsFromEnd = 1;
    2402         161 :         for (size_t i = dims.size(); i > 0;)
    2403             :         {
    2404         105 :             --i;
    2405             :             const auto nCurBlockSize =
    2406         105 :                 anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
    2407         105 :             const auto nMul = nMaxChunkMemory / nCurBlockSize;
    2408         105 :             if (nMul >= 2)
    2409             :             {
    2410          97 :                 const auto nSizeThisDim(dims[i]->GetSize());
    2411             :                 const auto nBlocksThisDim =
    2412          97 :                     DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
    2413          97 :                 anChunkSize[i] = static_cast<size_t>(std::min(
    2414          97 :                     anChunkSize[i] *
    2415         194 :                         std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
    2416          97 :                     nSizeThisDim));
    2417             :             }
    2418         105 :             nVoxelsFromEnd *= anChunkSize[i];
    2419             :         }
    2420             :     }
    2421          60 :     return anChunkSize;
    2422             : }
    2423             : 
    2424             : /************************************************************************/
    2425             : /*                         BaseRename()                                 */
    2426             : /************************************************************************/
    2427             : 
    2428             : //! @cond Doxygen_Suppress
    2429          18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
    2430             : {
    2431          18 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
    2432          18 :     m_osFullName += osNewName;
    2433          18 :     m_osName = osNewName;
    2434             : 
    2435          18 :     NotifyChildrenOfRenaming();
    2436          18 : }
    2437             : 
    2438             : //! @endcond
    2439             : 
    2440             : //! @cond Doxygen_Suppress
    2441             : /************************************************************************/
    2442             : /*                          ParentRenamed()                             */
    2443             : /************************************************************************/
    2444             : 
    2445          50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
    2446             : {
    2447          50 :     m_osFullName = osNewParentFullName;
    2448          50 :     m_osFullName += "/";
    2449          50 :     m_osFullName += m_osName;
    2450             : 
    2451          50 :     NotifyChildrenOfRenaming();
    2452          50 : }
    2453             : 
    2454             : //! @endcond
    2455             : 
    2456             : /************************************************************************/
    2457             : /*                             Deleted()                                */
    2458             : /************************************************************************/
    2459             : 
    2460             : //! @cond Doxygen_Suppress
    2461          52 : void GDALAbstractMDArray::Deleted()
    2462             : {
    2463          52 :     m_bValid = false;
    2464             : 
    2465          52 :     NotifyChildrenOfDeletion();
    2466          52 : }
    2467             : 
    2468             : //! @endcond
    2469             : 
    2470             : /************************************************************************/
    2471             : /*                        ParentDeleted()                               */
    2472             : /************************************************************************/
    2473             : 
    2474             : //! @cond Doxygen_Suppress
    2475          28 : void GDALAbstractMDArray::ParentDeleted()
    2476             : {
    2477          28 :     Deleted();
    2478          28 : }
    2479             : 
    2480             : //! @endcond
    2481             : 
    2482             : /************************************************************************/
    2483             : /*                     CheckValidAndErrorOutIfNot()                     */
    2484             : /************************************************************************/
    2485             : 
    2486             : //! @cond Doxygen_Suppress
    2487        5672 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
    2488             : {
    2489        5672 :     if (!m_bValid)
    2490             :     {
    2491          26 :         CPLError(CE_Failure, CPLE_AppDefined,
    2492             :                  "This object has been deleted. No action on it is possible");
    2493             :     }
    2494        5672 :     return m_bValid;
    2495             : }
    2496             : 
    2497             : //! @endcond
    2498             : 
    2499             : /************************************************************************/
    2500             : /*                             SetUnit()                                */
    2501             : /************************************************************************/
    2502             : 
    2503             : /** Set the variable unit.
    2504             :  *
    2505             :  * Values should conform as much as possible with those allowed by
    2506             :  * the NetCDF CF conventions:
    2507             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
    2508             :  * but others might be returned.
    2509             :  *
    2510             :  * Few examples are "meter", "degrees", "second", ...
    2511             :  * Empty value means unknown.
    2512             :  *
    2513             :  * This is the same as the C function GDALMDArraySetUnit()
    2514             :  *
    2515             :  * @note Driver implementation: optionally implemented.
    2516             :  *
    2517             :  * @param osUnit unit name.
    2518             :  * @return true in case of success.
    2519             :  */
    2520           0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
    2521             : {
    2522           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
    2523           0 :     return false;
    2524             : }
    2525             : 
    2526             : /************************************************************************/
    2527             : /*                             GetUnit()                                */
    2528             : /************************************************************************/
    2529             : 
    2530             : /** Return the array unit.
    2531             :  *
    2532             :  * Values should conform as much as possible with those allowed by
    2533             :  * the NetCDF CF conventions:
    2534             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
    2535             :  * but others might be returned.
    2536             :  *
    2537             :  * Few examples are "meter", "degrees", "second", ...
    2538             :  * Empty value means unknown.
    2539             :  *
    2540             :  * This is the same as the C function GDALMDArrayGetUnit()
    2541             :  */
    2542           5 : const std::string &GDALMDArray::GetUnit() const
    2543             : {
    2544           5 :     static const std::string emptyString;
    2545           5 :     return emptyString;
    2546             : }
    2547             : 
    2548             : /************************************************************************/
    2549             : /*                          SetSpatialRef()                             */
    2550             : /************************************************************************/
    2551             : 
    2552             : /** Assign a spatial reference system object to the array.
    2553             :  *
    2554             :  * This is the same as the C function GDALMDArraySetSpatialRef().
    2555             :  */
    2556           0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
    2557             : {
    2558           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
    2559           0 :     return false;
    2560             : }
    2561             : 
    2562             : /************************************************************************/
    2563             : /*                          GetSpatialRef()                             */
    2564             : /************************************************************************/
    2565             : 
    2566             : /** Return the spatial reference system object associated with the array.
    2567             :  *
    2568             :  * This is the same as the C function GDALMDArrayGetSpatialRef().
    2569             :  */
    2570           4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
    2571             : {
    2572           4 :     return nullptr;
    2573             : }
    2574             : 
    2575             : /************************************************************************/
    2576             : /*                        GetRawNoDataValue()                           */
    2577             : /************************************************************************/
    2578             : 
    2579             : /** Return the nodata value as a "raw" value.
    2580             :  *
    2581             :  * The value returned might be nullptr in case of no nodata value. When
    2582             :  * a nodata value is registered, a non-nullptr will be returned whose size in
    2583             :  * bytes is GetDataType().GetSize().
    2584             :  *
    2585             :  * The returned value should not be modified or freed. It is valid until
    2586             :  * the array is destroyed, or the next call to GetRawNoDataValue() or
    2587             :  * SetRawNoDataValue(), or any similar methods.
    2588             :  *
    2589             :  * @note Driver implementation: this method shall be implemented if nodata
    2590             :  * is supported.
    2591             :  *
    2592             :  * This is the same as the C function GDALMDArrayGetRawNoDataValue().
    2593             :  *
    2594             :  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
    2595             :  */
    2596           5 : const void *GDALMDArray::GetRawNoDataValue() const
    2597             : {
    2598           5 :     return nullptr;
    2599             : }
    2600             : 
    2601             : /************************************************************************/
    2602             : /*                        GetNoDataValueAsDouble()                      */
    2603             : /************************************************************************/
    2604             : 
    2605             : /** Return the nodata value as a double.
    2606             :  *
    2607             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
    2608             :  *
    2609             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2610             :  * a nodata value exists and can be converted to double. Might be nullptr.
    2611             :  *
    2612             :  * @return the nodata value as a double. A 0.0 value might also indicate the
    2613             :  * absence of a nodata value or an error in the conversion (*pbHasNoData will be
    2614             :  * set to false then).
    2615             :  */
    2616       22417 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
    2617             : {
    2618       22417 :     const void *pNoData = GetRawNoDataValue();
    2619       22417 :     double dfNoData = 0.0;
    2620       22417 :     const auto &eDT = GetDataType();
    2621       22417 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2622       22417 :     if (ok)
    2623             :     {
    2624       22180 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
    2625             :                         GDT_Float64, 0, 1);
    2626             :     }
    2627       22417 :     if (pbHasNoData)
    2628         384 :         *pbHasNoData = ok;
    2629       22417 :     return dfNoData;
    2630             : }
    2631             : 
    2632             : /************************************************************************/
    2633             : /*                        GetNoDataValueAsInt64()                       */
    2634             : /************************************************************************/
    2635             : 
    2636             : /** Return the nodata value as a Int64.
    2637             :  *
    2638             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2639             :  * a nodata value exists and can be converted to Int64. Might be nullptr.
    2640             :  *
    2641             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
    2642             :  *
    2643             :  * @return the nodata value as a Int64
    2644             :  *
    2645             :  * @since GDAL 3.5
    2646             :  */
    2647          12 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
    2648             : {
    2649          12 :     const void *pNoData = GetRawNoDataValue();
    2650          12 :     int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
    2651          12 :     const auto &eDT = GetDataType();
    2652          12 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2653          12 :     if (ok)
    2654             :     {
    2655           8 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
    2656             :                         GDT_Int64, 0, 1);
    2657             :     }
    2658          12 :     if (pbHasNoData)
    2659          12 :         *pbHasNoData = ok;
    2660          12 :     return nNoData;
    2661             : }
    2662             : 
    2663             : /************************************************************************/
    2664             : /*                       GetNoDataValueAsUInt64()                       */
    2665             : /************************************************************************/
    2666             : 
    2667             : /** Return the nodata value as a UInt64.
    2668             :  *
    2669             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
    2670             : 
    2671             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2672             :  * a nodata value exists and can be converted to UInt64. Might be nullptr.
    2673             :  *
    2674             :  * @return the nodata value as a UInt64
    2675             :  *
    2676             :  * @since GDAL 3.5
    2677             :  */
    2678           8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
    2679             : {
    2680           8 :     const void *pNoData = GetRawNoDataValue();
    2681           8 :     uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
    2682           8 :     const auto &eDT = GetDataType();
    2683           8 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2684           8 :     if (ok)
    2685             :     {
    2686           6 :         GDALCopyWords64(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
    2687             :                         GDT_UInt64, 0, 1);
    2688             :     }
    2689           8 :     if (pbHasNoData)
    2690           8 :         *pbHasNoData = ok;
    2691           8 :     return nNoData;
    2692             : }
    2693             : 
    2694             : /************************************************************************/
    2695             : /*                        SetRawNoDataValue()                           */
    2696             : /************************************************************************/
    2697             : 
    2698             : /** Set the nodata value as a "raw" value.
    2699             :  *
    2700             :  * The value passed might be nullptr in case of no nodata value. When
    2701             :  * a nodata value is registered, a non-nullptr whose size in
    2702             :  * bytes is GetDataType().GetSize() must be passed.
    2703             :  *
    2704             :  * This is the same as the C function GDALMDArraySetRawNoDataValue().
    2705             :  *
    2706             :  * @note Driver implementation: this method shall be implemented if setting
    2707             :  nodata
    2708             :  * is supported.
    2709             : 
    2710             :  * @return true in case of success.
    2711             :  */
    2712           0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
    2713             : {
    2714           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2715             :              "SetRawNoDataValue() not implemented");
    2716           0 :     return false;
    2717             : }
    2718             : 
    2719             : /************************************************************************/
    2720             : /*                           SetNoDataValue()                           */
    2721             : /************************************************************************/
    2722             : 
    2723             : /** Set the nodata value as a double.
    2724             :  *
    2725             :  * If the natural data type of the attribute/array is not double, type
    2726             :  * conversion will occur to the type returned by GetDataType().
    2727             :  *
    2728             :  * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
    2729             :  *
    2730             :  * @return true in case of success.
    2731             :  */
    2732          57 : bool GDALMDArray::SetNoDataValue(double dfNoData)
    2733             : {
    2734          57 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2735          57 :     bool bRet = false;
    2736          57 :     if (GDALExtendedDataType::CopyValue(
    2737         114 :             &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
    2738          57 :             GetDataType()))
    2739             :     {
    2740          57 :         bRet = SetRawNoDataValue(pRawNoData);
    2741             :     }
    2742          57 :     CPLFree(pRawNoData);
    2743          57 :     return bRet;
    2744             : }
    2745             : 
    2746             : /************************************************************************/
    2747             : /*                           SetNoDataValue()                           */
    2748             : /************************************************************************/
    2749             : 
    2750             : /** Set the nodata value as a Int64.
    2751             :  *
    2752             :  * If the natural data type of the attribute/array is not Int64, type conversion
    2753             :  * will occur to the type returned by GetDataType().
    2754             :  *
    2755             :  * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
    2756             :  *
    2757             :  * @return true in case of success.
    2758             :  *
    2759             :  * @since GDAL 3.5
    2760             :  */
    2761           3 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
    2762             : {
    2763           3 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2764           3 :     bool bRet = false;
    2765           3 :     if (GDALExtendedDataType::CopyValue(&nNoData,
    2766           6 :                                         GDALExtendedDataType::Create(GDT_Int64),
    2767           3 :                                         pRawNoData, GetDataType()))
    2768             :     {
    2769           3 :         bRet = SetRawNoDataValue(pRawNoData);
    2770             :     }
    2771           3 :     CPLFree(pRawNoData);
    2772           3 :     return bRet;
    2773             : }
    2774             : 
    2775             : /************************************************************************/
    2776             : /*                           SetNoDataValue()                           */
    2777             : /************************************************************************/
    2778             : 
    2779             : /** Set the nodata value as a Int64.
    2780             :  *
    2781             :  * If the natural data type of the attribute/array is not Int64, type conversion
    2782             :  * will occur to the type returned by GetDataType().
    2783             :  *
    2784             :  * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
    2785             :  *
    2786             :  * @return true in case of success.
    2787             :  *
    2788             :  * @since GDAL 3.5
    2789             :  */
    2790           1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
    2791             : {
    2792           1 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2793           1 :     bool bRet = false;
    2794           1 :     if (GDALExtendedDataType::CopyValue(
    2795           2 :             &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
    2796           1 :             GetDataType()))
    2797             :     {
    2798           1 :         bRet = SetRawNoDataValue(pRawNoData);
    2799             :     }
    2800           1 :     CPLFree(pRawNoData);
    2801           1 :     return bRet;
    2802             : }
    2803             : 
    2804             : /************************************************************************/
    2805             : /*                            Resize()                                  */
    2806             : /************************************************************************/
    2807             : 
    2808             : /** Resize an array to new dimensions.
    2809             :  *
    2810             :  * Not all drivers may allow this operation, and with restrictions (e.g.
    2811             :  * for netCDF, this is limited to growing of "unlimited" dimensions)
    2812             :  *
    2813             :  * Resizing a dimension used in other arrays will cause those other arrays
    2814             :  * to be resized.
    2815             :  *
    2816             :  * This is the same as the C function GDALMDArrayResize().
    2817             :  *
    2818             :  * @param anNewDimSizes Array of GetDimensionCount() values containing the
    2819             :  *                      new size of each indexing dimension.
    2820             :  * @param papszOptions Options. (Driver specific)
    2821             :  * @return true in case of success.
    2822             :  * @since GDAL 3.7
    2823             :  */
    2824           0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
    2825             :                          CPL_UNUSED CSLConstList papszOptions)
    2826             : {
    2827           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2828             :              "Resize() is not supported for this array");
    2829           0 :     return false;
    2830             : }
    2831             : 
    2832             : /************************************************************************/
    2833             : /*                               SetScale()                             */
    2834             : /************************************************************************/
    2835             : 
    2836             : /** Set the scale value to apply to raw values.
    2837             :  *
    2838             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2839             :  *
    2840             :  * This is the same as the C function GDALMDArraySetScale() /
    2841             :  * GDALMDArraySetScaleEx().
    2842             :  *
    2843             :  * @note Driver implementation: this method shall be implemented if setting
    2844             :  * scale is supported.
    2845             :  *
    2846             :  * @param dfScale scale
    2847             :  * @param eStorageType Data type to which create the potential attribute that
    2848             :  * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
    2849             :  * implementation will decide automatically the data type. Note that changing
    2850             :  * the data type after initial setting might not be supported.
    2851             :  * @return true in case of success.
    2852             :  */
    2853           0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
    2854             :                            CPL_UNUSED GDALDataType eStorageType)
    2855             : {
    2856           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
    2857           0 :     return false;
    2858             : }
    2859             : 
    2860             : /************************************************************************/
    2861             : /*                               SetOffset)                             */
    2862             : /************************************************************************/
    2863             : 
    2864             : /** Set the offset value to apply to raw values.
    2865             :  *
    2866             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2867             :  *
    2868             :  * This is the same as the C function GDALMDArraySetOffset() /
    2869             :  * GDALMDArraySetOffsetEx().
    2870             :  *
    2871             :  * @note Driver implementation: this method shall be implemented if setting
    2872             :  * offset is supported.
    2873             :  *
    2874             :  * @param dfOffset Offset
    2875             :  * @param eStorageType Data type to which create the potential attribute that
    2876             :  * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
    2877             :  * implementation will decide automatically the data type. Note that changing
    2878             :  * the data type after initial setting might not be supported.
    2879             :  * @return true in case of success.
    2880             :  */
    2881           0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
    2882             :                             CPL_UNUSED GDALDataType eStorageType)
    2883             : {
    2884           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
    2885           0 :     return false;
    2886             : }
    2887             : 
    2888             : /************************************************************************/
    2889             : /*                               GetScale()                             */
    2890             : /************************************************************************/
    2891             : 
    2892             : /** Get the scale value to apply to raw values.
    2893             :  *
    2894             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2895             :  *
    2896             :  * This is the same as the C function GDALMDArrayGetScale().
    2897             :  *
    2898             :  * @note Driver implementation: this method shall be implemented if gettings
    2899             :  * scale is supported.
    2900             :  *
    2901             :  * @param pbHasScale Pointer to a output boolean that will be set to true if
    2902             :  * a scale value exists. Might be nullptr.
    2903             :  * @param peStorageType Pointer to a output GDALDataType that will be set to
    2904             :  * the storage type of the scale value, when known/relevant. Otherwise will be
    2905             :  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
    2906             :  *
    2907             :  * @return the scale value. A 1.0 value might also indicate the
    2908             :  * absence of a scale value.
    2909             :  */
    2910          13 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
    2911             :                              CPL_UNUSED GDALDataType *peStorageType) const
    2912             : {
    2913          13 :     if (pbHasScale)
    2914          13 :         *pbHasScale = false;
    2915          13 :     return 1.0;
    2916             : }
    2917             : 
    2918             : /************************************************************************/
    2919             : /*                               GetOffset()                            */
    2920             : /************************************************************************/
    2921             : 
    2922             : /** Get the offset value to apply to raw values.
    2923             :  *
    2924             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2925             :  *
    2926             :  * This is the same as the C function GDALMDArrayGetOffset().
    2927             :  *
    2928             :  * @note Driver implementation: this method shall be implemented if gettings
    2929             :  * offset is supported.
    2930             :  *
    2931             :  * @param pbHasOffset Pointer to a output boolean that will be set to true if
    2932             :  * a offset value exists. Might be nullptr.
    2933             :  * @param peStorageType Pointer to a output GDALDataType that will be set to
    2934             :  * the storage type of the offset value, when known/relevant. Otherwise will be
    2935             :  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
    2936             :  *
    2937             :  * @return the offset value. A 0.0 value might also indicate the
    2938             :  * absence of a offset value.
    2939             :  */
    2940          13 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
    2941             :                               CPL_UNUSED GDALDataType *peStorageType) const
    2942             : {
    2943          13 :     if (pbHasOffset)
    2944          13 :         *pbHasOffset = false;
    2945          13 :     return 0.0;
    2946             : }
    2947             : 
    2948             : /************************************************************************/
    2949             : /*                         ProcessPerChunk()                            */
    2950             : /************************************************************************/
    2951             : 
    2952             : namespace
    2953             : {
    2954             : enum class Caller
    2955             : {
    2956             :     CALLER_END_OF_LOOP,
    2957             :     CALLER_IN_LOOP,
    2958             : };
    2959             : }
    2960             : 
    2961             : /** \brief Call a user-provided function to operate on an array chunk by chunk.
    2962             :  *
    2963             :  * This method is to be used when doing operations on an array, or a subset of
    2964             :  * it, in a chunk by chunk way.
    2965             :  *
    2966             :  * @param arrayStartIdx Values representing the starting index to use
    2967             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2968             :  *                      Array of GetDimensionCount() values. Must not be
    2969             :  *                      nullptr, unless for a zero-dimensional array.
    2970             :  *
    2971             :  * @param count         Values representing the number of values to use in
    2972             :  *                      each dimension.
    2973             :  *                      Array of GetDimensionCount() values. Must not be
    2974             :  *                      nullptr, unless for a zero-dimensional array.
    2975             :  *
    2976             :  * @param chunkSize     Values representing the chunk size in each dimension.
    2977             :  *                      Might typically the output of GetProcessingChunkSize().
    2978             :  *                      Array of GetDimensionCount() values. Must not be
    2979             :  *                      nullptr, unless for a zero-dimensional array.
    2980             :  *
    2981             :  * @param pfnFunc       User-provided function of type FuncProcessPerChunkType.
    2982             :  *                      Must NOT be nullptr.
    2983             :  *
    2984             :  * @param pUserData     Pointer to pass as the value of the pUserData argument
    2985             :  * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
    2986             :  *
    2987             :  * @return true in case of success.
    2988             :  */
    2989          58 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
    2990             :                                           const GUInt64 *count,
    2991             :                                           const size_t *chunkSize,
    2992             :                                           FuncProcessPerChunkType pfnFunc,
    2993             :                                           void *pUserData)
    2994             : {
    2995          58 :     const auto &dims = GetDimensions();
    2996          58 :     if (dims.empty())
    2997             :     {
    2998           2 :         return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
    2999             :     }
    3000             : 
    3001             :     // Sanity check
    3002          56 :     size_t nTotalChunkSize = 1;
    3003         146 :     for (size_t i = 0; i < dims.size(); i++)
    3004             :     {
    3005          97 :         const auto nSizeThisDim(dims[i]->GetSize());
    3006          97 :         if (count[i] == 0 || count[i] > nSizeThisDim ||
    3007          95 :             arrayStartIdx[i] > nSizeThisDim - count[i])
    3008             :         {
    3009           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    3010             :                      "Inconsistent arrayStartIdx[] / count[] values "
    3011             :                      "regarding array size");
    3012           4 :             return false;
    3013             :         }
    3014         184 :         if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
    3015          91 :             chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
    3016             :         {
    3017           3 :             CPLError(CE_Failure, CPLE_AppDefined,
    3018             :                      "Inconsistent chunkSize[] values");
    3019           3 :             return false;
    3020             :         }
    3021          90 :         nTotalChunkSize *= chunkSize[i];
    3022             :     }
    3023             : 
    3024          49 :     size_t dimIdx = 0;
    3025          98 :     std::vector<GUInt64> chunkArrayStartIdx(dims.size());
    3026          98 :     std::vector<size_t> chunkCount(dims.size());
    3027             : 
    3028             :     struct Stack
    3029             :     {
    3030             :         GUInt64 nBlockCounter = 0;
    3031             :         GUInt64 nBlocksMinusOne = 0;
    3032             :         size_t first_count = 0;  // only used if nBlocks > 1
    3033             :         Caller return_point = Caller::CALLER_END_OF_LOOP;
    3034             :     };
    3035             : 
    3036          98 :     std::vector<Stack> stack(dims.size());
    3037          49 :     GUInt64 iCurChunk = 0;
    3038          49 :     GUInt64 nChunkCount = 1;
    3039         138 :     for (size_t i = 0; i < dims.size(); i++)
    3040             :     {
    3041          89 :         const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
    3042          89 :         const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
    3043          89 :         stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
    3044          89 :         nChunkCount *= 1 + stack[i].nBlocksMinusOne;
    3045          89 :         if (stack[i].nBlocksMinusOne == 0)
    3046             :         {
    3047          84 :             chunkArrayStartIdx[i] = arrayStartIdx[i];
    3048          84 :             chunkCount[i] = static_cast<size_t>(count[i]);
    3049             :         }
    3050             :         else
    3051             :         {
    3052           5 :             stack[i].first_count = static_cast<size_t>(
    3053           5 :                 (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
    3054             :         }
    3055             :     }
    3056             : 
    3057          49 : lbl_next_depth:
    3058         248 :     if (dimIdx == dims.size())
    3059             :     {
    3060          82 :         ++iCurChunk;
    3061          82 :         if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
    3062             :                      iCurChunk, nChunkCount, pUserData))
    3063             :         {
    3064           0 :             return false;
    3065             :         }
    3066             :     }
    3067             :     else
    3068             :     {
    3069         166 :         if (stack[dimIdx].nBlocksMinusOne != 0)
    3070             :         {
    3071          11 :             stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
    3072          11 :             chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
    3073          11 :             chunkCount[dimIdx] = stack[dimIdx].first_count;
    3074          11 :             stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
    3075             :             while (true)
    3076             :             {
    3077          33 :                 dimIdx++;
    3078          33 :                 goto lbl_next_depth;
    3079          33 :             lbl_return_to_caller_in_loop:
    3080          33 :                 --stack[dimIdx].nBlockCounter;
    3081          33 :                 if (stack[dimIdx].nBlockCounter == 0)
    3082          11 :                     break;
    3083          22 :                 chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    3084          22 :                 chunkCount[dimIdx] = chunkSize[dimIdx];
    3085             :             }
    3086             : 
    3087          11 :             chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    3088          22 :             chunkCount[dimIdx] =
    3089          11 :                 static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
    3090          11 :                                     chunkArrayStartIdx[dimIdx]);
    3091          11 :             stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
    3092             :         }
    3093         166 :         dimIdx++;
    3094         166 :         goto lbl_next_depth;
    3095         166 :     lbl_return_to_caller_end_of_loop:
    3096         166 :         if (dimIdx == 0)
    3097          49 :             goto end;
    3098             :     }
    3099             : 
    3100         199 :     assert(dimIdx > 0);
    3101         199 :     dimIdx--;
    3102             :     // cppcheck-suppress negativeContainerIndex
    3103         199 :     switch (stack[dimIdx].return_point)
    3104             :     {
    3105         166 :         case Caller::CALLER_END_OF_LOOP:
    3106         166 :             goto lbl_return_to_caller_end_of_loop;
    3107          33 :         case Caller::CALLER_IN_LOOP:
    3108          33 :             goto lbl_return_to_caller_in_loop;
    3109             :     }
    3110          49 : end:
    3111          49 :     return true;
    3112             : }
    3113             : 
    3114             : /************************************************************************/
    3115             : /*                          GDALAttribute()                             */
    3116             : /************************************************************************/
    3117             : 
    3118             : //! @cond Doxygen_Suppress
    3119       13998 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
    3120           0 :                              CPL_UNUSED const std::string &osName)
    3121             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
    3122       13998 :     : GDALAbstractMDArray(osParentName, osName)
    3123             : #endif
    3124             : {
    3125       13998 : }
    3126             : 
    3127             : //! @endcond
    3128             : 
    3129             : /************************************************************************/
    3130             : /*                        GetDimensionSize()                            */
    3131             : /************************************************************************/
    3132             : 
    3133             : /** Return the size of the dimensions of the attribute.
    3134             :  *
    3135             :  * This will be an empty array for a scalar (single value) attribute.
    3136             :  *
    3137             :  * This is the same as the C function GDALAttributeGetDimensionsSize().
    3138             :  */
    3139         361 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
    3140             : {
    3141         361 :     const auto &dims = GetDimensions();
    3142         361 :     std::vector<GUInt64> ret;
    3143         361 :     ret.reserve(dims.size());
    3144         453 :     for (const auto &dim : dims)
    3145          92 :         ret.push_back(dim->GetSize());
    3146         361 :     return ret;
    3147             : }
    3148             : 
    3149             : /************************************************************************/
    3150             : /*                            GDALRawResult()                           */
    3151             : /************************************************************************/
    3152             : 
    3153             : //! @cond Doxygen_Suppress
    3154         149 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
    3155         149 :                              size_t nEltCount)
    3156         298 :     : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
    3157         149 :       m_raw(raw)
    3158             : {
    3159         149 : }
    3160             : 
    3161             : //! @endcond
    3162             : 
    3163             : /************************************************************************/
    3164             : /*                            GDALRawResult()                           */
    3165             : /************************************************************************/
    3166             : 
    3167             : /** Move constructor. */
    3168           0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
    3169           0 :     : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
    3170           0 :       m_nSize(other.m_nSize), m_raw(other.m_raw)
    3171             : {
    3172           0 :     other.m_nEltCount = 0;
    3173           0 :     other.m_nSize = 0;
    3174           0 :     other.m_raw = nullptr;
    3175           0 : }
    3176             : 
    3177             : /************************************************************************/
    3178             : /*                               FreeMe()                               */
    3179             : /************************************************************************/
    3180             : 
    3181         149 : void GDALRawResult::FreeMe()
    3182             : {
    3183         149 :     if (m_raw && m_dt.NeedsFreeDynamicMemory())
    3184             :     {
    3185          47 :         GByte *pabyPtr = m_raw;
    3186          47 :         const auto nDTSize(m_dt.GetSize());
    3187          94 :         for (size_t i = 0; i < m_nEltCount; ++i)
    3188             :         {
    3189          47 :             m_dt.FreeDynamicMemory(pabyPtr);
    3190          47 :             pabyPtr += nDTSize;
    3191             :         }
    3192             :     }
    3193         149 :     VSIFree(m_raw);
    3194         149 : }
    3195             : 
    3196             : /************************************************************************/
    3197             : /*                             operator=()                              */
    3198             : /************************************************************************/
    3199             : 
    3200             : /** Move assignment. */
    3201           0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
    3202             : {
    3203           0 :     FreeMe();
    3204           0 :     m_dt = std::move(other.m_dt);
    3205           0 :     m_nEltCount = other.m_nEltCount;
    3206           0 :     m_nSize = other.m_nSize;
    3207           0 :     m_raw = other.m_raw;
    3208           0 :     other.m_nEltCount = 0;
    3209           0 :     other.m_nSize = 0;
    3210           0 :     other.m_raw = nullptr;
    3211           0 :     return *this;
    3212             : }
    3213             : 
    3214             : /************************************************************************/
    3215             : /*                         ~GDALRawResult()                             */
    3216             : /************************************************************************/
    3217             : 
    3218             : /** Destructor. */
    3219         149 : GDALRawResult::~GDALRawResult()
    3220             : {
    3221         149 :     FreeMe();
    3222         149 : }
    3223             : 
    3224             : /************************************************************************/
    3225             : /*                            StealData()                               */
    3226             : /************************************************************************/
    3227             : 
    3228             : //! @cond Doxygen_Suppress
    3229             : /** Return buffer to caller which becomes owner of it.
    3230             :  * Only to be used by GDALAttributeReadAsRaw().
    3231             :  */
    3232           6 : GByte *GDALRawResult::StealData()
    3233             : {
    3234           6 :     GByte *ret = m_raw;
    3235           6 :     m_raw = nullptr;
    3236           6 :     m_nEltCount = 0;
    3237           6 :     m_nSize = 0;
    3238           6 :     return ret;
    3239             : }
    3240             : 
    3241             : //! @endcond
    3242             : 
    3243             : /************************************************************************/
    3244             : /*                             ReadAsRaw()                              */
    3245             : /************************************************************************/
    3246             : 
    3247             : /** Return the raw value of an attribute.
    3248             :  *
    3249             :  *
    3250             :  * This is the same as the C function GDALAttributeReadAsRaw().
    3251             :  */
    3252         149 : GDALRawResult GDALAttribute::ReadAsRaw() const
    3253             : {
    3254         149 :     const auto nEltCount(GetTotalElementsCount());
    3255         149 :     const auto &dt(GetDataType());
    3256         149 :     const auto nDTSize(dt.GetSize());
    3257             :     GByte *res = static_cast<GByte *>(
    3258         149 :         VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
    3259         149 :     if (!res)
    3260           0 :         return GDALRawResult(nullptr, dt, 0);
    3261         149 :     const auto &dims = GetDimensions();
    3262         149 :     const auto nDims = GetDimensionCount();
    3263         298 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3264         298 :     std::vector<size_t> count(1 + nDims);
    3265         168 :     for (size_t i = 0; i < nDims; i++)
    3266             :     {
    3267          19 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3268             :     }
    3269         149 :     if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
    3270         149 :               &res[0], static_cast<size_t>(nEltCount * nDTSize)))
    3271             :     {
    3272           0 :         VSIFree(res);
    3273           0 :         return GDALRawResult(nullptr, dt, 0);
    3274             :     }
    3275         149 :     return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
    3276             : }
    3277             : 
    3278             : /************************************************************************/
    3279             : /*                            ReadAsString()                            */
    3280             : /************************************************************************/
    3281             : 
    3282             : /** Return the value of an attribute as a string.
    3283             :  *
    3284             :  * The returned string should not be freed, and its lifetime does not
    3285             :  * excess a next call to ReadAsString() on the same object, or the deletion
    3286             :  * of the object itself.
    3287             :  *
    3288             :  * This function will only return the first element if there are several.
    3289             :  *
    3290             :  * This is the same as the C function GDALAttributeReadAsString()
    3291             :  *
    3292             :  * @return a string, or nullptr.
    3293             :  */
    3294        1302 : const char *GDALAttribute::ReadAsString() const
    3295             : {
    3296        1302 :     const auto nDims = GetDimensionCount();
    3297        2604 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3298        2604 :     std::vector<size_t> count(1 + nDims, 1);
    3299        1302 :     char *szRet = nullptr;
    3300        1302 :     if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
    3301        1302 :               GDALExtendedDataType::CreateString(), &szRet, &szRet,
    3302        3905 :               sizeof(szRet)) ||
    3303        1301 :         szRet == nullptr)
    3304             :     {
    3305           4 :         return nullptr;
    3306             :     }
    3307        1298 :     m_osCachedVal = szRet;
    3308        1298 :     CPLFree(szRet);
    3309        1298 :     return m_osCachedVal.c_str();
    3310             : }
    3311             : 
    3312             : /************************************************************************/
    3313             : /*                            ReadAsInt()                               */
    3314             : /************************************************************************/
    3315             : 
    3316             : /** Return the value of an attribute as a integer.
    3317             :  *
    3318             :  * This function will only return the first element if there are several.
    3319             :  *
    3320             :  * It can fail if its value can not be converted to integer.
    3321             :  *
    3322             :  * This is the same as the C function GDALAttributeReadAsInt()
    3323             :  *
    3324             :  * @return a integer, or INT_MIN in case of error.
    3325             :  */
    3326         218 : int GDALAttribute::ReadAsInt() const
    3327             : {
    3328         218 :     const auto nDims = GetDimensionCount();
    3329         436 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3330         218 :     std::vector<size_t> count(1 + nDims, 1);
    3331         218 :     int nRet = INT_MIN;
    3332         218 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3333         436 :          GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
    3334         436 :     return nRet;
    3335             : }
    3336             : 
    3337             : /************************************************************************/
    3338             : /*                            ReadAsInt64()                             */
    3339             : /************************************************************************/
    3340             : 
    3341             : /** Return the value of an attribute as an int64_t.
    3342             :  *
    3343             :  * This function will only return the first element if there are several.
    3344             :  *
    3345             :  * It can fail if its value can not be converted to long.
    3346             :  *
    3347             :  * This is the same as the C function GDALAttributeReadAsInt64()
    3348             :  *
    3349             :  * @return an int64_t, or INT64_MIN in case of error.
    3350             :  */
    3351          54 : int64_t GDALAttribute::ReadAsInt64() const
    3352             : {
    3353          54 :     const auto nDims = GetDimensionCount();
    3354         108 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3355          54 :     std::vector<size_t> count(1 + nDims, 1);
    3356          54 :     int64_t nRet = INT64_MIN;
    3357          54 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3358         108 :          GDALExtendedDataType::Create(GDT_Int64), &nRet, &nRet, sizeof(nRet));
    3359         108 :     return nRet;
    3360             : }
    3361             : 
    3362             : /************************************************************************/
    3363             : /*                            ReadAsDouble()                            */
    3364             : /************************************************************************/
    3365             : 
    3366             : /** Return the value of an attribute as a double.
    3367             :  *
    3368             :  * This function will only return the first element if there are several.
    3369             :  *
    3370             :  * It can fail if its value can not be converted to double.
    3371             :  *
    3372             :  * This is the same as the C function GDALAttributeReadAsInt()
    3373             :  *
    3374             :  * @return a double value.
    3375             :  */
    3376         339 : double GDALAttribute::ReadAsDouble() const
    3377             : {
    3378         339 :     const auto nDims = GetDimensionCount();
    3379         678 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3380         339 :     std::vector<size_t> count(1 + nDims, 1);
    3381         339 :     double dfRet = 0;
    3382         339 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3383         339 :          GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
    3384         339 :          sizeof(dfRet));
    3385         678 :     return dfRet;
    3386             : }
    3387             : 
    3388             : /************************************************************************/
    3389             : /*                          ReadAsStringArray()                         */
    3390             : /************************************************************************/
    3391             : 
    3392             : /** Return the value of an attribute as an array of strings.
    3393             :  *
    3394             :  * This is the same as the C function GDALAttributeReadAsStringArray()
    3395             :  */
    3396         104 : CPLStringList GDALAttribute::ReadAsStringArray() const
    3397             : {
    3398         104 :     const auto nElts = GetTotalElementsCount();
    3399         104 :     if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
    3400           0 :         return CPLStringList();
    3401             :     char **papszList = static_cast<char **>(
    3402         104 :         VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
    3403         104 :     const auto &dims = GetDimensions();
    3404         104 :     const auto nDims = GetDimensionCount();
    3405         208 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3406         208 :     std::vector<size_t> count(1 + nDims);
    3407         157 :     for (size_t i = 0; i < nDims; i++)
    3408             :     {
    3409          53 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3410             :     }
    3411         104 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3412         104 :          GDALExtendedDataType::CreateString(), papszList, papszList,
    3413         104 :          sizeof(char *) * static_cast<int>(nElts));
    3414         269 :     for (int i = 0; i < static_cast<int>(nElts); i++)
    3415             :     {
    3416         165 :         if (papszList[i] == nullptr)
    3417          13 :             papszList[i] = CPLStrdup("");
    3418             :     }
    3419         104 :     return CPLStringList(papszList);
    3420             : }
    3421             : 
    3422             : /************************************************************************/
    3423             : /*                          ReadAsIntArray()                            */
    3424             : /************************************************************************/
    3425             : 
    3426             : /** Return the value of an attribute as an array of integers.
    3427             :  *
    3428             :  * This is the same as the C function GDALAttributeReadAsIntArray().
    3429             :  */
    3430          15 : std::vector<int> GDALAttribute::ReadAsIntArray() const
    3431             : {
    3432          15 :     const auto nElts = GetTotalElementsCount();
    3433             : #if SIZEOF_VOIDP == 4
    3434             :     if (nElts > static_cast<size_t>(nElts))
    3435             :         return {};
    3436             : #endif
    3437          15 :     std::vector<int> res(static_cast<size_t>(nElts));
    3438          15 :     const auto &dims = GetDimensions();
    3439          15 :     const auto nDims = GetDimensionCount();
    3440          30 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3441          30 :     std::vector<size_t> count(1 + nDims);
    3442          32 :     for (size_t i = 0; i < nDims; i++)
    3443             :     {
    3444          17 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3445             :     }
    3446          15 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3447          30 :          GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
    3448          15 :          res.size() * sizeof(res[0]));
    3449          30 :     return res;
    3450             : }
    3451             : 
    3452             : /************************************************************************/
    3453             : /*                          ReadAsInt64Array()                          */
    3454             : /************************************************************************/
    3455             : 
    3456             : /** Return the value of an attribute as an array of int64_t.
    3457             :  *
    3458             :  * This is the same as the C function GDALAttributeReadAsInt64Array().
    3459             :  */
    3460          38 : std::vector<int64_t> GDALAttribute::ReadAsInt64Array() const
    3461             : {
    3462          38 :     const auto nElts = GetTotalElementsCount();
    3463             : #if SIZEOF_VOIDP == 4
    3464             :     if (nElts > static_cast<size_t>(nElts))
    3465             :         return {};
    3466             : #endif
    3467          38 :     std::vector<int64_t> res(static_cast<size_t>(nElts));
    3468          38 :     const auto &dims = GetDimensions();
    3469          38 :     const auto nDims = GetDimensionCount();
    3470          76 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3471          76 :     std::vector<size_t> count(1 + nDims);
    3472          76 :     for (size_t i = 0; i < nDims; i++)
    3473             :     {
    3474          38 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3475             :     }
    3476          38 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3477          76 :          GDALExtendedDataType::Create(GDT_Int64), &res[0], res.data(),
    3478          38 :          res.size() * sizeof(res[0]));
    3479          76 :     return res;
    3480             : }
    3481             : 
    3482             : /************************************************************************/
    3483             : /*                         ReadAsDoubleArray()                          */
    3484             : /************************************************************************/
    3485             : 
    3486             : /** Return the value of an attribute as an array of double.
    3487             :  *
    3488             :  * This is the same as the C function GDALAttributeReadAsDoubleArray().
    3489             :  */
    3490          87 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
    3491             : {
    3492          87 :     const auto nElts = GetTotalElementsCount();
    3493             : #if SIZEOF_VOIDP == 4
    3494             :     if (nElts > static_cast<size_t>(nElts))
    3495             :         return {};
    3496             : #endif
    3497          87 :     std::vector<double> res(static_cast<size_t>(nElts));
    3498          87 :     const auto &dims = GetDimensions();
    3499          87 :     const auto nDims = GetDimensionCount();
    3500         174 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3501         174 :     std::vector<size_t> count(1 + nDims);
    3502         158 :     for (size_t i = 0; i < nDims; i++)
    3503             :     {
    3504          71 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3505             :     }
    3506          87 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3507         174 :          GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
    3508          87 :          res.size() * sizeof(res[0]));
    3509         174 :     return res;
    3510             : }
    3511             : 
    3512             : /************************************************************************/
    3513             : /*                               Write()                                */
    3514             : /************************************************************************/
    3515             : 
    3516             : /** Write an attribute from raw values expressed in GetDataType()
    3517             :  *
    3518             :  * The values should be provided in the type of GetDataType() and there should
    3519             :  * be exactly GetTotalElementsCount() of them.
    3520             :  * If GetDataType() is a string, each value should be a char* pointer.
    3521             :  *
    3522             :  * This is the same as the C function GDALAttributeWriteRaw().
    3523             :  *
    3524             :  * @param pabyValue Buffer of nLen bytes.
    3525             :  * @param nLen Size of pabyValue in bytes. Should be equal to
    3526             :  *             GetTotalElementsCount() * GetDataType().GetSize()
    3527             :  * @return true in case of success.
    3528             :  */
    3529          91 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
    3530             : {
    3531          91 :     if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
    3532             :     {
    3533           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3534             :                  "Length is not of expected value");
    3535           0 :         return false;
    3536             :     }
    3537          91 :     const auto &dims = GetDimensions();
    3538          91 :     const auto nDims = GetDimensionCount();
    3539         182 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3540         182 :     std::vector<size_t> count(1 + nDims);
    3541         114 :     for (size_t i = 0; i < nDims; i++)
    3542             :     {
    3543          23 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3544             :     }
    3545          91 :     return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
    3546          91 :                  pabyValue, pabyValue, nLen);
    3547             : }
    3548             : 
    3549             : /************************************************************************/
    3550             : /*                               Write()                                */
    3551             : /************************************************************************/
    3552             : 
    3553             : /** Write an attribute from a string value.
    3554             :  *
    3555             :  * Type conversion will be performed if needed. If the attribute contains
    3556             :  * multiple values, only the first one will be updated.
    3557             :  *
    3558             :  * This is the same as the C function GDALAttributeWriteString().
    3559             :  *
    3560             :  * @param pszValue Pointer to a string.
    3561             :  * @return true in case of success.
    3562             :  */
    3563         304 : bool GDALAttribute::Write(const char *pszValue)
    3564             : {
    3565         304 :     const auto nDims = GetDimensionCount();
    3566         608 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3567         304 :     std::vector<size_t> count(1 + nDims, 1);
    3568         304 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3569         608 :                  GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
    3570         608 :                  sizeof(pszValue));
    3571             : }
    3572             : 
    3573             : /************************************************************************/
    3574             : /*                              WriteInt()                              */
    3575             : /************************************************************************/
    3576             : 
    3577             : /** Write an attribute from a integer value.
    3578             :  *
    3579             :  * Type conversion will be performed if needed. If the attribute contains
    3580             :  * multiple values, only the first one will be updated.
    3581             :  *
    3582             :  * This is the same as the C function GDALAttributeWriteInt().
    3583             :  *
    3584             :  * @param nVal Value.
    3585             :  * @return true in case of success.
    3586             :  */
    3587          22 : bool GDALAttribute::WriteInt(int nVal)
    3588             : {
    3589          22 :     const auto nDims = GetDimensionCount();
    3590          44 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3591          22 :     std::vector<size_t> count(1 + nDims, 1);
    3592          22 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3593          44 :                  GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
    3594          44 :                  sizeof(nVal));
    3595             : }
    3596             : 
    3597             : /************************************************************************/
    3598             : /*                              WriteInt64()                             */
    3599             : /************************************************************************/
    3600             : 
    3601             : /** Write an attribute from an int64_t value.
    3602             :  *
    3603             :  * Type conversion will be performed if needed. If the attribute contains
    3604             :  * multiple values, only the first one will be updated.
    3605             :  *
    3606             :  * This is the same as the C function GDALAttributeWriteInt().
    3607             :  *
    3608             :  * @param nVal Value.
    3609             :  * @return true in case of success.
    3610             :  */
    3611          11 : bool GDALAttribute::WriteInt64(int64_t nVal)
    3612             : {
    3613          11 :     const auto nDims = GetDimensionCount();
    3614          22 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3615          11 :     std::vector<size_t> count(1 + nDims, 1);
    3616          11 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3617          22 :                  GDALExtendedDataType::Create(GDT_Int64), &nVal, &nVal,
    3618          22 :                  sizeof(nVal));
    3619             : }
    3620             : 
    3621             : /************************************************************************/
    3622             : /*                                Write()                               */
    3623             : /************************************************************************/
    3624             : 
    3625             : /** Write an attribute from a double value.
    3626             :  *
    3627             :  * Type conversion will be performed if needed. If the attribute contains
    3628             :  * multiple values, only the first one will be updated.
    3629             :  *
    3630             :  * This is the same as the C function GDALAttributeWriteDouble().
    3631             :  *
    3632             :  * @param dfVal Value.
    3633             :  * @return true in case of success.
    3634             :  */
    3635          36 : bool GDALAttribute::Write(double dfVal)
    3636             : {
    3637          36 :     const auto nDims = GetDimensionCount();
    3638          72 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3639          36 :     std::vector<size_t> count(1 + nDims, 1);
    3640          36 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3641          72 :                  GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
    3642          72 :                  sizeof(dfVal));
    3643             : }
    3644             : 
    3645             : /************************************************************************/
    3646             : /*                                Write()                               */
    3647             : /************************************************************************/
    3648             : 
    3649             : /** Write an attribute from an array of strings.
    3650             :  *
    3651             :  * Type conversion will be performed if needed.
    3652             :  *
    3653             :  * Exactly GetTotalElementsCount() strings must be provided
    3654             :  *
    3655             :  * This is the same as the C function GDALAttributeWriteStringArray().
    3656             :  *
    3657             :  * @param vals Array of strings.
    3658             :  * @return true in case of success.
    3659             :  */
    3660           8 : bool GDALAttribute::Write(CSLConstList vals)
    3661             : {
    3662           8 :     if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
    3663             :     {
    3664           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3665           1 :         return false;
    3666             :     }
    3667           7 :     const auto nDims = GetDimensionCount();
    3668          14 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3669           7 :     std::vector<size_t> count(1 + nDims);
    3670           7 :     const auto &dims = GetDimensions();
    3671          15 :     for (size_t i = 0; i < nDims; i++)
    3672           8 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3673           7 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3674           7 :                  GDALExtendedDataType::CreateString(), vals, vals,
    3675          14 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
    3676             : }
    3677             : 
    3678             : /************************************************************************/
    3679             : /*                                Write()                               */
    3680             : /************************************************************************/
    3681             : 
    3682             : /** Write an attribute from an array of int.
    3683             :  *
    3684             :  * Type conversion will be performed if needed.
    3685             :  *
    3686             :  * Exactly GetTotalElementsCount() strings must be provided
    3687             :  *
    3688             :  * This is the same as the C function GDALAttributeWriteIntArray()
    3689             :  *
    3690             :  * @param vals Array of int.
    3691             :  * @param nVals Should be equal to GetTotalElementsCount().
    3692             :  * @return true in case of success.
    3693             :  */
    3694           9 : bool GDALAttribute::Write(const int *vals, size_t nVals)
    3695             : {
    3696           9 :     if (nVals != GetTotalElementsCount())
    3697             :     {
    3698           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3699           1 :         return false;
    3700             :     }
    3701           8 :     const auto nDims = GetDimensionCount();
    3702          16 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3703           8 :     std::vector<size_t> count(1 + nDims);
    3704           8 :     const auto &dims = GetDimensions();
    3705          16 :     for (size_t i = 0; i < nDims; i++)
    3706           8 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3707           8 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3708           8 :                  GDALExtendedDataType::Create(GDT_Int32), vals, vals,
    3709          16 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(GInt32));
    3710             : }
    3711             : 
    3712             : /************************************************************************/
    3713             : /*                                Write()                               */
    3714             : /************************************************************************/
    3715             : 
    3716             : /** Write an attribute from an array of int64_t.
    3717             :  *
    3718             :  * Type conversion will be performed if needed.
    3719             :  *
    3720             :  * Exactly GetTotalElementsCount() strings must be provided
    3721             :  *
    3722             :  * This is the same as the C function GDALAttributeWriteLongArray()
    3723             :  *
    3724             :  * @param vals Array of int64_t.
    3725             :  * @param nVals Should be equal to GetTotalElementsCount().
    3726             :  * @return true in case of success.
    3727             :  */
    3728          10 : bool GDALAttribute::Write(const int64_t *vals, size_t nVals)
    3729             : {
    3730          10 :     if (nVals != GetTotalElementsCount())
    3731             :     {
    3732           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3733           0 :         return false;
    3734             :     }
    3735          10 :     const auto nDims = GetDimensionCount();
    3736          20 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3737          10 :     std::vector<size_t> count(1 + nDims);
    3738          10 :     const auto &dims = GetDimensions();
    3739          20 :     for (size_t i = 0; i < nDims; i++)
    3740          10 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3741          10 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3742          10 :                  GDALExtendedDataType::Create(GDT_Int64), vals, vals,
    3743          10 :                  static_cast<size_t>(GetTotalElementsCount()) *
    3744          10 :                      sizeof(int64_t));
    3745             : }
    3746             : 
    3747             : /************************************************************************/
    3748             : /*                                Write()                               */
    3749             : /************************************************************************/
    3750             : 
    3751             : /** Write an attribute from an array of double.
    3752             :  *
    3753             :  * Type conversion will be performed if needed.
    3754             :  *
    3755             :  * Exactly GetTotalElementsCount() strings must be provided
    3756             :  *
    3757             :  * This is the same as the C function GDALAttributeWriteDoubleArray()
    3758             :  *
    3759             :  * @param vals Array of double.
    3760             :  * @param nVals Should be equal to GetTotalElementsCount().
    3761             :  * @return true in case of success.
    3762             :  */
    3763           7 : bool GDALAttribute::Write(const double *vals, size_t nVals)
    3764             : {
    3765           7 :     if (nVals != GetTotalElementsCount())
    3766             :     {
    3767           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3768           1 :         return false;
    3769             :     }
    3770           6 :     const auto nDims = GetDimensionCount();
    3771          12 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3772           6 :     std::vector<size_t> count(1 + nDims);
    3773           6 :     const auto &dims = GetDimensions();
    3774          13 :     for (size_t i = 0; i < nDims; i++)
    3775           7 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3776           6 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3777           6 :                  GDALExtendedDataType::Create(GDT_Float64), vals, vals,
    3778          12 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
    3779             : }
    3780             : 
    3781             : /************************************************************************/
    3782             : /*                           GDALMDArray()                              */
    3783             : /************************************************************************/
    3784             : 
    3785             : //! @cond Doxygen_Suppress
    3786        6213 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
    3787             :                          CPL_UNUSED const std::string &osName,
    3788           0 :                          const std::string &osContext)
    3789             :     :
    3790             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
    3791             :       GDALAbstractMDArray(osParentName, osName),
    3792             : #endif
    3793        6213 :       m_osContext(osContext)
    3794             : {
    3795        6213 : }
    3796             : 
    3797             : //! @endcond
    3798             : 
    3799             : /************************************************************************/
    3800             : /*                           GetTotalCopyCost()                         */
    3801             : /************************************************************************/
    3802             : 
    3803             : /** Return a total "cost" to copy the array.
    3804             :  *
    3805             :  * Used as a parameter for CopyFrom()
    3806             :  */
    3807          43 : GUInt64 GDALMDArray::GetTotalCopyCost() const
    3808             : {
    3809          86 :     return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
    3810          86 :            GetTotalElementsCount() * GetDataType().GetSize();
    3811             : }
    3812             : 
    3813             : /************************************************************************/
    3814             : /*                       CopyFromAllExceptValues()                      */
    3815             : /************************************************************************/
    3816             : 
    3817             : //! @cond Doxygen_Suppress
    3818             : 
    3819         144 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
    3820             :                                           bool bStrict, GUInt64 &nCurCost,
    3821             :                                           const GUInt64 nTotalCost,
    3822             :                                           GDALProgressFunc pfnProgress,
    3823             :                                           void *pProgressData)
    3824             : {
    3825             :     // Nodata setting must be one of the first things done for TileDB
    3826         144 :     const void *pNoData = poSrcArray->GetRawNoDataValue();
    3827         144 :     if (pNoData && poSrcArray->GetDataType() == GetDataType())
    3828             :     {
    3829          13 :         SetRawNoDataValue(pNoData);
    3830             :     }
    3831             : 
    3832         144 :     const bool bThisIsUnscaledArray =
    3833         144 :         dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
    3834         288 :     auto attrs = poSrcArray->GetAttributes();
    3835         191 :     for (const auto &attr : attrs)
    3836             :     {
    3837          47 :         const auto &osAttrName = attr->GetName();
    3838          47 :         if (bThisIsUnscaledArray)
    3839             :         {
    3840           6 :             if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
    3841           7 :                 osAttrName == "valid_min" || osAttrName == "valid_max" ||
    3842           1 :                 osAttrName == "valid_range")
    3843             :             {
    3844           1 :                 continue;
    3845             :             }
    3846             :         }
    3847             : 
    3848          46 :         auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
    3849          92 :                                        attr->GetDataType());
    3850          46 :         if (!dstAttr)
    3851             :         {
    3852           0 :             if (bStrict)
    3853           0 :                 return false;
    3854           0 :             continue;
    3855             :         }
    3856          46 :         auto raw = attr->ReadAsRaw();
    3857          46 :         if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
    3858           0 :             return false;
    3859             :     }
    3860         144 :     if (!attrs.empty())
    3861             :     {
    3862          26 :         nCurCost += attrs.size() * GDALAttribute::COPY_COST;
    3863          46 :         if (pfnProgress &&
    3864          20 :             !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    3865           0 :             return false;
    3866             :     }
    3867             : 
    3868         144 :     auto srcSRS = poSrcArray->GetSpatialRef();
    3869         144 :     if (srcSRS)
    3870             :     {
    3871          11 :         SetSpatialRef(srcSRS.get());
    3872             :     }
    3873             : 
    3874         144 :     const std::string &osUnit(poSrcArray->GetUnit());
    3875         144 :     if (!osUnit.empty())
    3876             :     {
    3877          18 :         SetUnit(osUnit);
    3878             :     }
    3879             : 
    3880         144 :     bool bGotValue = false;
    3881         144 :     GDALDataType eOffsetStorageType = GDT_Unknown;
    3882             :     const double dfOffset =
    3883         144 :         poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
    3884         144 :     if (bGotValue)
    3885             :     {
    3886           3 :         SetOffset(dfOffset, eOffsetStorageType);
    3887             :     }
    3888             : 
    3889         144 :     bGotValue = false;
    3890         144 :     GDALDataType eScaleStorageType = GDT_Unknown;
    3891         144 :     const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
    3892         144 :     if (bGotValue)
    3893             :     {
    3894           3 :         SetScale(dfScale, eScaleStorageType);
    3895             :     }
    3896             : 
    3897         144 :     return true;
    3898             : }
    3899             : 
    3900             : //! @endcond
    3901             : 
    3902             : /************************************************************************/
    3903             : /*                               CopyFrom()                             */
    3904             : /************************************************************************/
    3905             : 
    3906             : /** Copy the content of an array into a new (generally empty) array.
    3907             :  *
    3908             :  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
    3909             :  *                   of some output drivers this is not recommended)
    3910             :  * @param poSrcArray Source array. Should NOT be nullptr.
    3911             :  * @param bStrict Whether to enable strict mode. In strict mode, any error will
    3912             :  *                stop the copy. In relaxed mode, the copy will be attempted to
    3913             :  *                be pursued.
    3914             :  * @param nCurCost  Should be provided as a variable initially set to 0.
    3915             :  * @param nTotalCost Total cost from GetTotalCopyCost().
    3916             :  * @param pfnProgress Progress callback, or nullptr.
    3917             :  * @param pProgressData Progress user data, or nulptr.
    3918             :  *
    3919             :  * @return true in case of success (or partial success if bStrict == false).
    3920             :  */
    3921          41 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
    3922             :                            const GDALMDArray *poSrcArray, bool bStrict,
    3923             :                            GUInt64 &nCurCost, const GUInt64 nTotalCost,
    3924             :                            GDALProgressFunc pfnProgress, void *pProgressData)
    3925             : {
    3926          41 :     if (pfnProgress == nullptr)
    3927           4 :         pfnProgress = GDALDummyProgress;
    3928             : 
    3929          41 :     nCurCost += GDALMDArray::COPY_COST;
    3930             : 
    3931          41 :     if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
    3932             :                                  pfnProgress, pProgressData))
    3933             :     {
    3934           0 :         return false;
    3935             :     }
    3936             : 
    3937          41 :     const auto &dims = poSrcArray->GetDimensions();
    3938          41 :     const auto nDTSize = poSrcArray->GetDataType().GetSize();
    3939          41 :     if (dims.empty())
    3940             :     {
    3941           2 :         std::vector<GByte> abyTmp(nDTSize);
    3942           2 :         if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
    3943           2 :                                GetDataType(), &abyTmp[0]) &&
    3944           2 :               Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
    3945           4 :                     &abyTmp[0])) &&
    3946             :             bStrict)
    3947             :         {
    3948           0 :             return false;
    3949             :         }
    3950           2 :         nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
    3951           2 :         if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    3952           0 :             return false;
    3953             :     }
    3954             :     else
    3955             :     {
    3956          39 :         std::vector<GUInt64> arrayStartIdx(dims.size());
    3957          39 :         std::vector<GUInt64> count(dims.size());
    3958         106 :         for (size_t i = 0; i < dims.size(); i++)
    3959             :         {
    3960          67 :             count[i] = static_cast<size_t>(dims[i]->GetSize());
    3961             :         }
    3962             : 
    3963             :         struct CopyFunc
    3964             :         {
    3965             :             GDALMDArray *poDstArray = nullptr;
    3966             :             std::vector<GByte> abyTmp{};
    3967             :             GDALProgressFunc pfnProgress = nullptr;
    3968             :             void *pProgressData = nullptr;
    3969             :             GUInt64 nCurCost = 0;
    3970             :             GUInt64 nTotalCost = 0;
    3971             :             GUInt64 nTotalBytesThisArray = 0;
    3972             :             bool bStop = false;
    3973             : 
    3974          57 :             static bool f(GDALAbstractMDArray *l_poSrcArray,
    3975             :                           const GUInt64 *chunkArrayStartIdx,
    3976             :                           const size_t *chunkCount, GUInt64 iCurChunk,
    3977             :                           GUInt64 nChunkCount, void *pUserData)
    3978             :             {
    3979          57 :                 const auto &dt(l_poSrcArray->GetDataType());
    3980          57 :                 auto data = static_cast<CopyFunc *>(pUserData);
    3981          57 :                 auto poDstArray = data->poDstArray;
    3982          57 :                 if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
    3983          57 :                                         nullptr, dt, &data->abyTmp[0]))
    3984             :                 {
    3985           0 :                     return false;
    3986             :                 }
    3987             :                 bool bRet =
    3988          57 :                     poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
    3989          57 :                                       nullptr, dt, &data->abyTmp[0]);
    3990          57 :                 if (dt.NeedsFreeDynamicMemory())
    3991             :                 {
    3992           2 :                     const auto l_nDTSize = dt.GetSize();
    3993           2 :                     GByte *ptr = &data->abyTmp[0];
    3994           2 :                     const size_t l_nDims(l_poSrcArray->GetDimensionCount());
    3995           2 :                     size_t nEltCount = 1;
    3996           4 :                     for (size_t i = 0; i < l_nDims; ++i)
    3997             :                     {
    3998           2 :                         nEltCount *= chunkCount[i];
    3999             :                     }
    4000          10 :                     for (size_t i = 0; i < nEltCount; i++)
    4001             :                     {
    4002           8 :                         dt.FreeDynamicMemory(ptr);
    4003           8 :                         ptr += l_nDTSize;
    4004             :                     }
    4005             :                 }
    4006          57 :                 if (!bRet)
    4007             :                 {
    4008           0 :                     return false;
    4009             :                 }
    4010             : 
    4011          57 :                 double dfCurCost =
    4012          57 :                     double(data->nCurCost) + double(iCurChunk) / nChunkCount *
    4013          57 :                                                  data->nTotalBytesThisArray;
    4014          57 :                 if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
    4015             :                                        data->pProgressData))
    4016             :                 {
    4017           0 :                     data->bStop = true;
    4018           0 :                     return false;
    4019             :                 }
    4020             : 
    4021          57 :                 return true;
    4022             :             }
    4023             :         };
    4024             : 
    4025          39 :         CopyFunc copyFunc;
    4026          39 :         copyFunc.poDstArray = this;
    4027          39 :         copyFunc.nCurCost = nCurCost;
    4028          39 :         copyFunc.nTotalCost = nTotalCost;
    4029          39 :         copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
    4030          39 :         copyFunc.pfnProgress = pfnProgress;
    4031          39 :         copyFunc.pProgressData = pProgressData;
    4032             :         const char *pszSwathSize =
    4033          39 :             CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
    4034             :         const size_t nMaxChunkSize =
    4035             :             pszSwathSize
    4036          39 :                 ? static_cast<size_t>(
    4037           1 :                       std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    4038           1 :                                CPLAtoGIntBig(pszSwathSize)))
    4039             :                 : static_cast<size_t>(
    4040          38 :                       std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    4041          38 :                                GDALGetCacheMax64() / 4));
    4042          39 :         const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
    4043          39 :         size_t nRealChunkSize = nDTSize;
    4044         106 :         for (const auto &nChunkSize : anChunkSizes)
    4045             :         {
    4046          67 :             nRealChunkSize *= nChunkSize;
    4047             :         }
    4048             :         try
    4049             :         {
    4050          39 :             copyFunc.abyTmp.resize(nRealChunkSize);
    4051             :         }
    4052           0 :         catch (const std::exception &)
    4053             :         {
    4054           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    4055             :                      "Cannot allocate temporary buffer");
    4056           0 :             nCurCost += copyFunc.nTotalBytesThisArray;
    4057           0 :             return false;
    4058             :         }
    4059         116 :         if (copyFunc.nTotalBytesThisArray != 0 &&
    4060          38 :             !const_cast<GDALMDArray *>(poSrcArray)
    4061          38 :                  ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
    4062             :                                    anChunkSizes.data(), CopyFunc::f,
    4063          77 :                                    &copyFunc) &&
    4064           0 :             (bStrict || copyFunc.bStop))
    4065             :         {
    4066           0 :             nCurCost += copyFunc.nTotalBytesThisArray;
    4067           0 :             return false;
    4068             :         }
    4069          39 :         nCurCost += copyFunc.nTotalBytesThisArray;
    4070             :     }
    4071             : 
    4072          41 :     return true;
    4073             : }
    4074             : 
    4075             : /************************************************************************/
    4076             : /*                         GetStructuralInfo()                          */
    4077             : /************************************************************************/
    4078             : 
    4079             : /** Return structural information on the array.
    4080             :  *
    4081             :  * This may be the compression, etc..
    4082             :  *
    4083             :  * The return value should not be freed and is valid until GDALMDArray is
    4084             :  * released or this function called again.
    4085             :  *
    4086             :  * This is the same as the C function GDALMDArrayGetStructuralInfo().
    4087             :  */
    4088          55 : CSLConstList GDALMDArray::GetStructuralInfo() const
    4089             : {
    4090          55 :     return nullptr;
    4091             : }
    4092             : 
    4093             : /************************************************************************/
    4094             : /*                          AdviseRead()                                */
    4095             : /************************************************************************/
    4096             : 
    4097             : /** Advise driver of upcoming read requests.
    4098             :  *
    4099             :  * Some GDAL drivers operate more efficiently if they know in advance what
    4100             :  * set of upcoming read requests will be made.  The AdviseRead() method allows
    4101             :  * an application to notify the driver of the region of interest.
    4102             :  *
    4103             :  * Many drivers just ignore the AdviseRead() call, but it can dramatically
    4104             :  * accelerate access via some drivers. One such case is when reading through
    4105             :  * a DAP dataset with the netCDF driver (a in-memory cache array is then created
    4106             :  * with the region of interest defined by AdviseRead())
    4107             :  *
    4108             :  * This is the same as the C function GDALMDArrayAdviseRead().
    4109             :  *
    4110             :  * @param arrayStartIdx Values representing the starting index to read
    4111             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    4112             :  *                      Array of GetDimensionCount() values.
    4113             :  *                      Can be nullptr as a synonymous for [0 for i in
    4114             :  * range(GetDimensionCount() ]
    4115             :  *
    4116             :  * @param count         Values representing the number of values to extract in
    4117             :  *                      each dimension.
    4118             :  *                      Array of GetDimensionCount() values.
    4119             :  *                      Can be nullptr as a synonymous for
    4120             :  *                      [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
    4121             :  * range(GetDimensionCount() ]
    4122             :  *
    4123             :  * @param papszOptions Driver specific options, or nullptr. Consult driver
    4124             :  * documentation.
    4125             :  *
    4126             :  * @return true in case of success (ignoring the advice is a success)
    4127             :  *
    4128             :  * @since GDAL 3.2
    4129             :  */
    4130          25 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    4131             :                              CSLConstList papszOptions) const
    4132             : {
    4133          25 :     const auto nDimCount = GetDimensionCount();
    4134          25 :     if (nDimCount == 0)
    4135           2 :         return true;
    4136             : 
    4137          46 :     std::vector<GUInt64> tmp_arrayStartIdx;
    4138          23 :     if (arrayStartIdx == nullptr)
    4139             :     {
    4140           0 :         tmp_arrayStartIdx.resize(nDimCount);
    4141           0 :         arrayStartIdx = tmp_arrayStartIdx.data();
    4142             :     }
    4143             : 
    4144          46 :     std::vector<size_t> tmp_count;
    4145          23 :     if (count == nullptr)
    4146             :     {
    4147           0 :         tmp_count.resize(nDimCount);
    4148           0 :         const auto &dims = GetDimensions();
    4149           0 :         for (size_t i = 0; i < nDimCount; i++)
    4150             :         {
    4151           0 :             const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
    4152             : #if SIZEOF_VOIDP < 8
    4153             :             if (nSize != static_cast<size_t>(nSize))
    4154             :             {
    4155             :                 CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
    4156             :                 return false;
    4157             :             }
    4158             : #endif
    4159           0 :             tmp_count[i] = static_cast<size_t>(nSize);
    4160             :         }
    4161           0 :         count = tmp_count.data();
    4162             :     }
    4163             : 
    4164          46 :     std::vector<GInt64> tmp_arrayStep;
    4165          46 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    4166          23 :     const GInt64 *arrayStep = nullptr;
    4167          23 :     const GPtrDiff_t *bufferStride = nullptr;
    4168          23 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    4169          46 :                               GDALExtendedDataType::Create(GDT_Unknown),
    4170             :                               nullptr, nullptr, 0, tmp_arrayStep,
    4171             :                               tmp_bufferStride))
    4172             :     {
    4173           1 :         return false;
    4174             :     }
    4175             : 
    4176          22 :     return IAdviseRead(arrayStartIdx, count, papszOptions);
    4177             : }
    4178             : 
    4179             : /************************************************************************/
    4180             : /*                             IAdviseRead()                            */
    4181             : /************************************************************************/
    4182             : 
    4183             : //! @cond Doxygen_Suppress
    4184           3 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
    4185             :                               CSLConstList /* papszOptions*/) const
    4186             : {
    4187           3 :     return true;
    4188             : }
    4189             : 
    4190             : //! @endcond
    4191             : 
    4192             : /************************************************************************/
    4193             : /*                            MassageName()                             */
    4194             : /************************************************************************/
    4195             : 
    4196             : //! @cond Doxygen_Suppress
    4197          32 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
    4198             : {
    4199          32 :     std::string ret;
    4200         604 :     for (const char ch : inputName)
    4201             :     {
    4202         572 :         if (!isalnum(static_cast<unsigned char>(ch)))
    4203         138 :             ret += '_';
    4204             :         else
    4205         434 :             ret += ch;
    4206             :     }
    4207          32 :     return ret;
    4208             : }
    4209             : 
    4210             : //! @endcond
    4211             : 
    4212             : /************************************************************************/
    4213             : /*                         GetCacheRootGroup()                          */
    4214             : /************************************************************************/
    4215             : 
    4216             : //! @cond Doxygen_Suppress
    4217             : std::shared_ptr<GDALGroup>
    4218        1345 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
    4219             :                                std::string &osCacheFilenameOut) const
    4220             : {
    4221        1345 :     const auto &osFilename = GetFilename();
    4222        1345 :     if (osFilename.empty())
    4223             :     {
    4224           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4225             :                  "Cannot cache an array with an empty filename");
    4226           1 :         return nullptr;
    4227             :     }
    4228             : 
    4229        1344 :     osCacheFilenameOut = osFilename + ".gmac";
    4230        1344 :     if (STARTS_WITH(osFilename.c_str(), "/vsicurl/http"))
    4231             :     {
    4232           0 :         const auto nPosQuestionMark = osFilename.find('?');
    4233           0 :         if (nPosQuestionMark != std::string::npos)
    4234             :         {
    4235             :             osCacheFilenameOut =
    4236           0 :                 osFilename.substr(0, nPosQuestionMark)
    4237           0 :                     .append(".gmac")
    4238           0 :                     .append(osFilename.substr(nPosQuestionMark));
    4239             :         }
    4240             :     }
    4241        1344 :     const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
    4242        1344 :     if (pszProxy != nullptr)
    4243           7 :         osCacheFilenameOut = pszProxy;
    4244             : 
    4245        1344 :     std::unique_ptr<GDALDataset> poDS;
    4246             :     VSIStatBufL sStat;
    4247        1344 :     if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
    4248             :     {
    4249          28 :         poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
    4250             :                                      GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
    4251             :                                      nullptr, nullptr, nullptr));
    4252             :     }
    4253        1344 :     if (poDS)
    4254             :     {
    4255          28 :         CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
    4256          28 :         return poDS->GetRootGroup();
    4257             :     }
    4258             : 
    4259        1316 :     if (bCanCreate)
    4260             :     {
    4261           4 :         const char *pszDrvName = "netCDF";
    4262           4 :         GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
    4263           4 :         if (poDrv == nullptr)
    4264             :         {
    4265           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
    4266             :                      pszDrvName);
    4267           0 :             return nullptr;
    4268             :         }
    4269             :         {
    4270           8 :             CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
    4271           8 :             CPLErrorStateBackuper oErrorStateBackuper;
    4272           4 :             poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
    4273             :                                                      nullptr, nullptr));
    4274             :         }
    4275           4 :         if (!poDS)
    4276             :         {
    4277           1 :             pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
    4278           1 :             if (pszProxy)
    4279             :             {
    4280           1 :                 osCacheFilenameOut = pszProxy;
    4281           1 :                 poDS.reset(poDrv->CreateMultiDimensional(
    4282             :                     osCacheFilenameOut.c_str(), nullptr, nullptr));
    4283             :             }
    4284             :         }
    4285           4 :         if (poDS)
    4286             :         {
    4287           4 :             CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
    4288           4 :             return poDS->GetRootGroup();
    4289             :         }
    4290             :         else
    4291             :         {
    4292           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4293             :                      "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
    4294             :                      "configuration option to write the cache in "
    4295             :                      "another directory",
    4296             :                      osCacheFilenameOut.c_str());
    4297             :         }
    4298             :     }
    4299             : 
    4300        1312 :     return nullptr;
    4301             : }
    4302             : 
    4303             : //! @endcond
    4304             : 
    4305             : /************************************************************************/
    4306             : /*                              Cache()                                 */
    4307             : /************************************************************************/
    4308             : 
    4309             : /** Cache the content of the array into an auxiliary filename.
    4310             :  *
    4311             :  * The main purpose of this method is to be able to cache views that are
    4312             :  * expensive to compute, such as transposed arrays.
    4313             :  *
    4314             :  * The array will be stored in a file whose name is the one of
    4315             :  * GetFilename(), with an extra .gmac extension (stands for GDAL
    4316             :  * Multidimensional Array Cache). The cache is a netCDF dataset.
    4317             :  *
    4318             :  * If the .gmac file cannot be written next to the dataset, the
    4319             :  * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
    4320             :  * directory.
    4321             :  *
    4322             :  * The GDALMDArray::Read() method will automatically use the cache when it
    4323             :  * exists. There is no timestamp checks between the source array and the cached
    4324             :  * array. If the source arrays changes, the cache must be manually deleted.
    4325             :  *
    4326             :  * This is the same as the C function GDALMDArrayCache()
    4327             :  *
    4328             :  * @note Driver implementation: optionally implemented.
    4329             :  *
    4330             :  * @param papszOptions List of options, null terminated, or NULL. Currently
    4331             :  *                     the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
    4332             :  *                     to specify the block size of the cached array.
    4333             :  * @return true in case of success.
    4334             :  */
    4335           7 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
    4336             : {
    4337          14 :     std::string osCacheFilename;
    4338          14 :     auto poRG = GetCacheRootGroup(true, osCacheFilename);
    4339           7 :     if (!poRG)
    4340           1 :         return false;
    4341             : 
    4342          12 :     const std::string osCachedArrayName(MassageName(GetFullName()));
    4343           6 :     if (poRG->OpenMDArray(osCachedArrayName))
    4344             :     {
    4345           2 :         CPLError(CE_Failure, CPLE_NotSupported,
    4346             :                  "An array with same name %s already exists in %s",
    4347             :                  osCachedArrayName.c_str(), osCacheFilename.c_str());
    4348           2 :         return false;
    4349             :     }
    4350             : 
    4351           8 :     CPLStringList aosOptions;
    4352           4 :     aosOptions.SetNameValue("COMPRESS", "DEFLATE");
    4353           4 :     const auto &aoDims = GetDimensions();
    4354           8 :     std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
    4355           4 :     if (!aoDims.empty())
    4356             :     {
    4357             :         std::string osBlockSize(
    4358           4 :             CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
    4359           4 :         if (osBlockSize.empty())
    4360             :         {
    4361           6 :             const auto anBlockSize = GetBlockSize();
    4362           3 :             int idxDim = 0;
    4363          10 :             for (auto nBlockSize : anBlockSize)
    4364             :             {
    4365           7 :                 if (idxDim > 0)
    4366           4 :                     osBlockSize += ',';
    4367           7 :                 if (nBlockSize == 0)
    4368           7 :                     nBlockSize = 256;
    4369           7 :                 nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
    4370             :                 osBlockSize +=
    4371           7 :                     std::to_string(static_cast<uint64_t>(nBlockSize));
    4372           7 :                 idxDim++;
    4373             :             }
    4374             :         }
    4375           4 :         aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
    4376             : 
    4377           4 :         int idxDim = 0;
    4378          13 :         for (const auto &poDim : aoDims)
    4379             :         {
    4380           9 :             auto poNewDim = poRG->CreateDimension(
    4381          18 :                 osCachedArrayName + '_' + std::to_string(idxDim),
    4382          18 :                 poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
    4383           9 :             if (!poNewDim)
    4384           0 :                 return false;
    4385           9 :             aoNewDims.emplace_back(poNewDim);
    4386           9 :             idxDim++;
    4387             :         }
    4388             :     }
    4389             : 
    4390           4 :     auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
    4391           8 :                                              GetDataType(), aosOptions.List());
    4392           4 :     if (!poCachedArray)
    4393             :     {
    4394           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
    4395             :                  osCachedArrayName.c_str(), osCacheFilename.c_str());
    4396           0 :         return false;
    4397             :     }
    4398             : 
    4399           4 :     GUInt64 nCost = 0;
    4400           8 :     return poCachedArray->CopyFrom(nullptr, this,
    4401             :                                    false,  // strict
    4402           4 :                                    nCost, GetTotalCopyCost(), nullptr, nullptr);
    4403             : }
    4404             : 
    4405             : /************************************************************************/
    4406             : /*                               Read()                                 */
    4407             : /************************************************************************/
    4408             : 
    4409        3728 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
    4410             :                        const GInt64 *arrayStep,         // step in elements
    4411             :                        const GPtrDiff_t *bufferStride,  // stride in elements
    4412             :                        const GDALExtendedDataType &bufferDataType,
    4413             :                        void *pDstBuffer, const void *pDstBufferAllocStart,
    4414             :                        size_t nDstBufferAllocSize) const
    4415             : {
    4416        3728 :     if (!m_bHasTriedCachedArray)
    4417             :     {
    4418        1636 :         m_bHasTriedCachedArray = true;
    4419        1636 :         if (IsCacheable())
    4420             :         {
    4421        1636 :             const auto &osFilename = GetFilename();
    4422        2778 :             if (!osFilename.empty() &&
    4423        2778 :                 !EQUAL(CPLGetExtensionSafe(osFilename.c_str()).c_str(), "gmac"))
    4424             :             {
    4425        2264 :                 std::string osCacheFilename;
    4426        2264 :                 auto poRG = GetCacheRootGroup(false, osCacheFilename);
    4427        1132 :                 if (poRG)
    4428             :                 {
    4429             :                     const std::string osCachedArrayName(
    4430          32 :                         MassageName(GetFullName()));
    4431          16 :                     m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
    4432          16 :                     if (m_poCachedArray)
    4433             :                     {
    4434           6 :                         const auto &dims = GetDimensions();
    4435             :                         const auto &cachedDims =
    4436           6 :                             m_poCachedArray->GetDimensions();
    4437           6 :                         const size_t nDims = dims.size();
    4438             :                         bool ok =
    4439          12 :                             m_poCachedArray->GetDataType() == GetDataType() &&
    4440           6 :                             cachedDims.size() == nDims;
    4441          19 :                         for (size_t i = 0; ok && i < nDims; ++i)
    4442             :                         {
    4443          13 :                             ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
    4444             :                         }
    4445           6 :                         if (ok)
    4446             :                         {
    4447           6 :                             CPLDebug("GDAL", "Cached array for %s found in %s",
    4448             :                                      osCachedArrayName.c_str(),
    4449             :                                      osCacheFilename.c_str());
    4450             :                         }
    4451             :                         else
    4452             :                         {
    4453           0 :                             CPLError(CE_Warning, CPLE_AppDefined,
    4454             :                                      "Cached array %s in %s has incompatible "
    4455             :                                      "characteristics with current array.",
    4456             :                                      osCachedArrayName.c_str(),
    4457             :                                      osCacheFilename.c_str());
    4458           0 :                             m_poCachedArray.reset();
    4459             :                         }
    4460             :                     }
    4461             :                 }
    4462             :             }
    4463             :         }
    4464             :     }
    4465             : 
    4466        3728 :     const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
    4467        3728 :     if (!array->GetDataType().CanConvertTo(bufferDataType))
    4468             :     {
    4469           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4470             :                  "Array data type is not convertible to buffer data type");
    4471           0 :         return false;
    4472             :     }
    4473             : 
    4474        7456 :     std::vector<GInt64> tmp_arrayStep;
    4475        7456 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    4476        3728 :     if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
    4477             :                                      bufferStride, bufferDataType, pDstBuffer,
    4478             :                                      pDstBufferAllocStart, nDstBufferAllocSize,
    4479             :                                      tmp_arrayStep, tmp_bufferStride))
    4480             :     {
    4481           9 :         return false;
    4482             :     }
    4483             : 
    4484        3719 :     return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
    4485        3719 :                         bufferDataType, pDstBuffer);
    4486             : }
    4487             : 
    4488             : /************************************************************************/
    4489             : /*                          GetRootGroup()                              */
    4490             : /************************************************************************/
    4491             : 
    4492             : /** Return the root group to which this arrays belongs too.
    4493             :  *
    4494             :  * Note that arrays may be free standing and some drivers may not implement
    4495             :  * this method, hence nullptr may be returned.
    4496             :  *
    4497             :  * It is used internally by the GetResampled() method to detect if GLT
    4498             :  * orthorectification is available.
    4499             :  *
    4500             :  * @return the root group, or nullptr.
    4501             :  * @since GDAL 3.8
    4502             :  */
    4503           0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
    4504             : {
    4505           0 :     return nullptr;
    4506             : }
    4507             : 
    4508             : //! @cond Doxygen_Suppress
    4509             : 
    4510             : /************************************************************************/
    4511             : /*                       IsTransposedRequest()                          */
    4512             : /************************************************************************/
    4513             : 
    4514         689 : bool GDALMDArray::IsTransposedRequest(
    4515             :     const size_t *count,
    4516             :     const GPtrDiff_t *bufferStride) const  // stride in elements
    4517             : {
    4518             :     /*
    4519             :     For example:
    4520             :     count = [2,3,4]
    4521             :     strides = [12, 4, 1]            (2-1)*12+(3-1)*4+(4-1)*1=23   ==> row major
    4522             :     stride [12, 1, 3]            (2-1)*12+(3-1)*1+(4-1)*3=23   ==>
    4523             :     (axis[0],axis[2],axis[1]) transposition [1, 8, 2]             (2-1)*1+
    4524             :     (3-1)*8+(4-1)*2=23   ==> (axis[2],axis[1],axis[0]) transposition
    4525             :     */
    4526         689 :     const size_t nDims(GetDimensionCount());
    4527         689 :     size_t nCurStrideForRowMajorStrides = 1;
    4528         689 :     bool bRowMajorStrides = true;
    4529         689 :     size_t nElts = 1;
    4530         689 :     size_t nLastIdx = 0;
    4531        1954 :     for (size_t i = nDims; i > 0;)
    4532             :     {
    4533        1265 :         --i;
    4534        1265 :         if (bufferStride[i] < 0)
    4535           0 :             return false;
    4536        1265 :         if (static_cast<size_t>(bufferStride[i]) !=
    4537             :             nCurStrideForRowMajorStrides)
    4538             :         {
    4539         221 :             bRowMajorStrides = false;
    4540             :         }
    4541             :         // Integer overflows have already been checked in CheckReadWriteParams()
    4542        1265 :         nCurStrideForRowMajorStrides *= count[i];
    4543        1265 :         nElts *= count[i];
    4544        1265 :         nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
    4545             :     }
    4546         689 :     if (bRowMajorStrides)
    4547         540 :         return false;
    4548         149 :     return nLastIdx == nElts - 1;
    4549             : }
    4550             : 
    4551             : /************************************************************************/
    4552             : /*                   CopyToFinalBufferSameDataType()                    */
    4553             : /************************************************************************/
    4554             : 
    4555             : template <size_t N>
    4556          60 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
    4557             :                                    size_t nDims, const size_t *count,
    4558             :                                    const GPtrDiff_t *bufferStride)
    4559             : {
    4560         120 :     std::vector<size_t> anStackCount(nDims);
    4561         120 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    4562          60 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
    4563             : #if defined(__GNUC__)
    4564             : #pragma GCC diagnostic push
    4565             : #pragma GCC diagnostic ignored "-Wnull-dereference"
    4566             : #endif
    4567          60 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    4568             : #if defined(__GNUC__)
    4569             : #pragma GCC diagnostic pop
    4570             : #endif
    4571          60 :     size_t iDim = 0;
    4572             : 
    4573         749 : lbl_next_depth:
    4574         749 :     if (iDim == nDims - 1)
    4575             :     {
    4576         661 :         size_t n = count[iDim];
    4577         661 :         GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
    4578         661 :         const auto bufferStrideLastDim = bufferStride[iDim] * N;
    4579       29186 :         while (n > 0)
    4580             :         {
    4581       28525 :             --n;
    4582       28525 :             memcpy(pabyDstBuffer, pabySrcBuffer, N);
    4583       28525 :             pabyDstBuffer += bufferStrideLastDim;
    4584       28525 :             pabySrcBuffer += N;
    4585             :         }
    4586             :     }
    4587             :     else
    4588             :     {
    4589          88 :         anStackCount[iDim] = count[iDim];
    4590             :         while (true)
    4591             :         {
    4592         689 :             ++iDim;
    4593         689 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    4594         689 :             goto lbl_next_depth;
    4595         689 :         lbl_return_to_caller_in_loop:
    4596         689 :             --iDim;
    4597         689 :             --anStackCount[iDim];
    4598         689 :             if (anStackCount[iDim] == 0)
    4599          88 :                 break;
    4600         601 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
    4601             :         }
    4602             :     }
    4603         749 :     if (iDim > 0)
    4604         689 :         goto lbl_return_to_caller_in_loop;
    4605          60 : }
    4606             : 
    4607             : /************************************************************************/
    4608             : /*                        CopyToFinalBuffer()                           */
    4609             : /************************************************************************/
    4610             : 
    4611         129 : static void CopyToFinalBuffer(const void *pSrcBuffer,
    4612             :                               const GDALExtendedDataType &eSrcDataType,
    4613             :                               void *pDstBuffer,
    4614             :                               const GDALExtendedDataType &eDstDataType,
    4615             :                               size_t nDims, const size_t *count,
    4616             :                               const GPtrDiff_t *bufferStride)
    4617             : {
    4618         129 :     const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
    4619             :     // Use specialized implementation for well-known data types when no
    4620             :     // type conversion is needed
    4621         129 :     if (eSrcDataType == eDstDataType)
    4622             :     {
    4623         110 :         if (nSrcDataTypeSize == 1)
    4624             :         {
    4625          41 :             CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
    4626             :                                              count, bufferStride);
    4627          60 :             return;
    4628             :         }
    4629          69 :         else if (nSrcDataTypeSize == 2)
    4630             :         {
    4631           1 :             CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
    4632             :                                              count, bufferStride);
    4633           1 :             return;
    4634             :         }
    4635          68 :         else if (nSrcDataTypeSize == 4)
    4636             :         {
    4637          14 :             CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
    4638             :                                              count, bufferStride);
    4639          14 :             return;
    4640             :         }
    4641          54 :         else if (nSrcDataTypeSize == 8)
    4642             :         {
    4643           4 :             CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
    4644             :                                              count, bufferStride);
    4645           4 :             return;
    4646             :         }
    4647             :     }
    4648             : 
    4649          69 :     const size_t nDstDataTypeSize(eDstDataType.GetSize());
    4650         138 :     std::vector<size_t> anStackCount(nDims);
    4651         138 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    4652          69 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
    4653          69 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    4654          69 :     size_t iDim = 0;
    4655             : 
    4656         338 : lbl_next_depth:
    4657         338 :     if (iDim == nDims - 1)
    4658             :     {
    4659         328 :         GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
    4660         328 :                                          pabyDstBufferStack[iDim], eDstDataType,
    4661         328 :                                          bufferStride[iDim], count[iDim]);
    4662         328 :         pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
    4663             :     }
    4664             :     else
    4665             :     {
    4666          10 :         anStackCount[iDim] = count[iDim];
    4667             :         while (true)
    4668             :         {
    4669         269 :             ++iDim;
    4670         269 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    4671         269 :             goto lbl_next_depth;
    4672         269 :         lbl_return_to_caller_in_loop:
    4673         269 :             --iDim;
    4674         269 :             --anStackCount[iDim];
    4675         269 :             if (anStackCount[iDim] == 0)
    4676          10 :                 break;
    4677         259 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
    4678             :         }
    4679             :     }
    4680         338 :     if (iDim > 0)
    4681         269 :         goto lbl_return_to_caller_in_loop;
    4682             : }
    4683             : 
    4684             : /************************************************************************/
    4685             : /*                      TransposeLast2Dims()                            */
    4686             : /************************************************************************/
    4687             : 
    4688          19 : static bool TransposeLast2Dims(void *pDstBuffer,
    4689             :                                const GDALExtendedDataType &eDT,
    4690             :                                const size_t nDims, const size_t *count,
    4691             :                                const size_t nEltsNonLast2Dims)
    4692             : {
    4693          19 :     const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
    4694          19 :     const auto nDTSize = eDT.GetSize();
    4695             :     void *pTempBufferForLast2DimsTranspose =
    4696          19 :         VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
    4697          19 :     if (pTempBufferForLast2DimsTranspose == nullptr)
    4698           0 :         return false;
    4699             : 
    4700          19 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
    4701          58 :     for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
    4702             :     {
    4703          39 :         GDALTranspose2D(pabyDstBuffer, eDT.GetNumericDataType(),
    4704             :                         pTempBufferForLast2DimsTranspose,
    4705          39 :                         eDT.GetNumericDataType(), count[nDims - 1],
    4706          39 :                         count[nDims - 2]);
    4707          39 :         memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
    4708             :                nDTSize * nEltsLast2Dims);
    4709          39 :         pabyDstBuffer += nDTSize * nEltsLast2Dims;
    4710             :     }
    4711             : 
    4712          19 :     VSIFree(pTempBufferForLast2DimsTranspose);
    4713             : 
    4714          19 :     return true;
    4715             : }
    4716             : 
    4717             : /************************************************************************/
    4718             : /*                      ReadForTransposedRequest()                      */
    4719             : /************************************************************************/
    4720             : 
    4721             : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
    4722             : // transposed view yield to extremely poor/unusable performance. This fixes
    4723             : // this by using temporary memory to read in a contiguous buffer in a
    4724             : // row-major order, and then do the transposition to the final buffer.
    4725             : 
    4726         148 : bool GDALMDArray::ReadForTransposedRequest(
    4727             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    4728             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
    4729             :     void *pDstBuffer) const
    4730             : {
    4731         148 :     const size_t nDims(GetDimensionCount());
    4732         148 :     if (nDims == 0)
    4733             :     {
    4734           0 :         CPLAssert(false);
    4735             :         return false;  // shouldn't happen
    4736             :     }
    4737         148 :     size_t nElts = 1;
    4738         418 :     for (size_t i = 0; i < nDims; ++i)
    4739         270 :         nElts *= count[i];
    4740             : 
    4741         296 :     std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
    4742         148 :     tmpBufferStrides.back() = 1;
    4743         270 :     for (size_t i = nDims - 1; i > 0;)
    4744             :     {
    4745         122 :         --i;
    4746         122 :         tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
    4747             :     }
    4748             : 
    4749         148 :     const auto &eDT = GetDataType();
    4750         148 :     const auto nDTSize = eDT.GetSize();
    4751         277 :     if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
    4752         293 :         static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
    4753          16 :         (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
    4754             :     {
    4755             :         // Optimization of the optimization if only the last 2 dims are
    4756             :         // transposed that saves on temporary buffer allocation
    4757          23 :         const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
    4758          23 :         size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
    4759          23 :         bool bRowMajorStridesForNonLast2Dims = true;
    4760          23 :         size_t nEltsNonLast2Dims = 1;
    4761          40 :         for (size_t i = nDims - 2; i > 0;)
    4762             :         {
    4763          17 :             --i;
    4764          17 :             if (static_cast<size_t>(bufferStride[i]) !=
    4765             :                 nCurStrideForRowMajorStrides)
    4766             :             {
    4767           4 :                 bRowMajorStridesForNonLast2Dims = false;
    4768             :             }
    4769             :             // Integer overflows have already been checked in
    4770             :             // CheckReadWriteParams()
    4771          17 :             nCurStrideForRowMajorStrides *= count[i];
    4772          17 :             nEltsNonLast2Dims *= count[i];
    4773             :         }
    4774          23 :         if (bRowMajorStridesForNonLast2Dims)
    4775             :         {
    4776             :             // We read in the final buffer!
    4777          19 :             if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
    4778          19 :                        eDT, pDstBuffer))
    4779             :             {
    4780           0 :                 return false;
    4781             :             }
    4782             : 
    4783          19 :             return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
    4784          19 :                                       nEltsNonLast2Dims);
    4785             :         }
    4786             :     }
    4787             : 
    4788         129 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
    4789         129 :     if (pTempBuffer == nullptr)
    4790           0 :         return false;
    4791             : 
    4792         129 :     if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
    4793         129 :                pTempBuffer))
    4794             :     {
    4795           0 :         VSIFree(pTempBuffer);
    4796           0 :         return false;
    4797             :     }
    4798         129 :     CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
    4799             :                       count, bufferStride);
    4800             : 
    4801         129 :     if (eDT.NeedsFreeDynamicMemory())
    4802             :     {
    4803          58 :         GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
    4804         116 :         for (size_t i = 0; i < nElts; ++i)
    4805             :         {
    4806          58 :             eDT.FreeDynamicMemory(pabyPtr);
    4807          58 :             pabyPtr += nDTSize;
    4808             :         }
    4809             :     }
    4810             : 
    4811         129 :     VSIFree(pTempBuffer);
    4812         129 :     return true;
    4813             : }
    4814             : 
    4815             : /************************************************************************/
    4816             : /*               IsStepOneContiguousRowMajorOrderedSameDataType()       */
    4817             : /************************************************************************/
    4818             : 
    4819             : // Returns true if at all following conditions are met:
    4820             : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
    4821             : // defines a row-major ordered contiguous buffer.
    4822          78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
    4823             :     const size_t *count, const GInt64 *arrayStep,
    4824             :     const GPtrDiff_t *bufferStride,
    4825             :     const GDALExtendedDataType &bufferDataType) const
    4826             : {
    4827          78 :     if (bufferDataType != GetDataType())
    4828           5 :         return false;
    4829          73 :     size_t nExpectedStride = 1;
    4830         166 :     for (size_t i = GetDimensionCount(); i > 0;)
    4831             :     {
    4832          96 :         --i;
    4833          96 :         if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
    4834          94 :             static_cast<size_t>(bufferStride[i]) != nExpectedStride)
    4835             :         {
    4836           3 :             return false;
    4837             :         }
    4838          93 :         nExpectedStride *= count[i];
    4839             :     }
    4840          70 :     return true;
    4841             : }
    4842             : 
    4843             : /************************************************************************/
    4844             : /*                      ReadUsingContiguousIRead()                      */
    4845             : /************************************************************************/
    4846             : 
    4847             : // Used for example by the TileDB driver when requesting it with
    4848             : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
    4849             : // not defining a row-major ordered contiguous buffer.
    4850             : // Should only be called when at least one of the above conditions are true,
    4851             : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
    4852             : // returning none.
    4853             : // This method will call IRead() again with arrayStep[] == 1,
    4854             : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
    4855             : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
    4856             : // content of that temporary buffer onto pDstBuffer.
    4857           7 : bool GDALMDArray::ReadUsingContiguousIRead(
    4858             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    4859             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
    4860             :     void *pDstBuffer) const
    4861             : {
    4862           7 :     const size_t nDims(GetDimensionCount());
    4863          14 :     std::vector<GUInt64> anTmpStartIdx(nDims);
    4864          14 :     std::vector<size_t> anTmpCount(nDims);
    4865           7 :     const auto &oType = GetDataType();
    4866           7 :     size_t nMemArraySize = oType.GetSize();
    4867          14 :     std::vector<GPtrDiff_t> anTmpStride(nDims);
    4868           7 :     GPtrDiff_t nStride = 1;
    4869          18 :     for (size_t i = nDims; i > 0;)
    4870             :     {
    4871          11 :         --i;
    4872          11 :         if (arrayStep[i] > 0)
    4873           9 :             anTmpStartIdx[i] = arrayStartIdx[i];
    4874             :         else
    4875           2 :             anTmpStartIdx[i] =
    4876           2 :                 arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
    4877             :         const uint64_t nCount =
    4878          11 :             (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
    4879          11 :         if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
    4880             :         {
    4881           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4882             :                      "Read() failed due to too large memory requirement");
    4883           0 :             return false;
    4884             :         }
    4885          11 :         anTmpCount[i] = static_cast<size_t>(nCount);
    4886          11 :         nMemArraySize *= anTmpCount[i];
    4887          11 :         anTmpStride[i] = nStride;
    4888          11 :         nStride *= anTmpCount[i];
    4889             :     }
    4890             :     std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
    4891          14 :         VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
    4892           7 :     if (!pTmpBuffer)
    4893           0 :         return false;
    4894           7 :     if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
    4895          14 :                std::vector<GInt64>(nDims, 1).data(),  // steps
    4896           7 :                anTmpStride.data(), oType, pTmpBuffer.get()))
    4897             :     {
    4898           0 :         return false;
    4899             :     }
    4900          14 :     std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
    4901          18 :     for (size_t i = 0; i < nDims; ++i)
    4902             :     {
    4903          11 :         if (arrayStep[i] > 0)
    4904           9 :             anTmpStartIdx[i] = 0;
    4905             :         else
    4906           2 :             anTmpStartIdx[i] = anTmpCount[i] - 1;
    4907          22 :         apoTmpDims[i] = std::make_shared<GDALDimension>(
    4908          22 :             std::string(), std::string(), std::string(), std::string(),
    4909          22 :             anTmpCount[i]);
    4910             :     }
    4911             :     auto poMEMArray =
    4912          14 :         MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
    4913          21 :     return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
    4914           7 :            poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
    4915           7 :                             bufferStride, bufferDataType, pDstBuffer);
    4916             : }
    4917             : 
    4918             : //! @endcond
    4919             : 
    4920             : /************************************************************************/
    4921             : /*                       GDALSlicedMDArray                              */
    4922             : /************************************************************************/
    4923             : 
    4924             : class GDALSlicedMDArray final : public GDALPamMDArray
    4925             : {
    4926             :   private:
    4927             :     std::shared_ptr<GDALMDArray> m_poParent{};
    4928             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    4929             :     std::vector<size_t> m_mapDimIdxToParentDimIdx{};  // of size m_dims.size()
    4930             :     std::vector<Range>
    4931             :         m_parentRanges{};  // of size m_poParent->GetDimensionCount()
    4932             : 
    4933             :     mutable std::vector<GUInt64> m_parentStart;
    4934             :     mutable std::vector<size_t> m_parentCount;
    4935             :     mutable std::vector<GInt64> m_parentStep;
    4936             :     mutable std::vector<GPtrDiff_t> m_parentStride;
    4937             : 
    4938             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
    4939             :                              const GInt64 *arrayStep,
    4940             :                              const GPtrDiff_t *bufferStride) const;
    4941             : 
    4942             :   protected:
    4943         574 :     explicit GDALSlicedMDArray(
    4944             :         const std::shared_ptr<GDALMDArray> &poParent,
    4945             :         const std::string &viewExpr,
    4946             :         std::vector<std::shared_ptr<GDALDimension>> &&dims,
    4947             :         std::vector<size_t> &&mapDimIdxToParentDimIdx,
    4948             :         std::vector<Range> &&parentRanges)
    4949        1722 :         : GDALAbstractMDArray(std::string(), "Sliced view of " +
    4950        1722 :                                                  poParent->GetFullName() +
    4951        1148 :                                                  " (" + viewExpr + ")"),
    4952        1148 :           GDALPamMDArray(std::string(),
    4953        1148 :                          "Sliced view of " + poParent->GetFullName() + " (" +
    4954        1148 :                              viewExpr + ")",
    4955        1148 :                          GDALPamMultiDim::GetPAM(poParent),
    4956             :                          poParent->GetContext()),
    4957        1148 :           m_poParent(std::move(poParent)), m_dims(std::move(dims)),
    4958         574 :           m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
    4959         574 :           m_parentRanges(std::move(parentRanges)),
    4960         574 :           m_parentStart(m_poParent->GetDimensionCount()),
    4961         574 :           m_parentCount(m_poParent->GetDimensionCount(), 1),
    4962         574 :           m_parentStep(m_poParent->GetDimensionCount()),
    4963        4592 :           m_parentStride(m_poParent->GetDimensionCount())
    4964             :     {
    4965         574 :     }
    4966             : 
    4967             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    4968             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    4969             :                const GDALExtendedDataType &bufferDataType,
    4970             :                void *pDstBuffer) const override;
    4971             : 
    4972             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    4973             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    4974             :                 const GDALExtendedDataType &bufferDataType,
    4975             :                 const void *pSrcBuffer) override;
    4976             : 
    4977             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    4978             :                      CSLConstList papszOptions) const override;
    4979             : 
    4980             :   public:
    4981             :     static std::shared_ptr<GDALSlicedMDArray>
    4982         574 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    4983             :            const std::string &viewExpr,
    4984             :            std::vector<std::shared_ptr<GDALDimension>> &&dims,
    4985             :            std::vector<size_t> &&mapDimIdxToParentDimIdx,
    4986             :            std::vector<Range> &&parentRanges)
    4987             :     {
    4988         574 :         CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
    4989         574 :         CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
    4990             : 
    4991             :         auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
    4992         574 :             poParent, viewExpr, std::move(dims),
    4993         574 :             std::move(mapDimIdxToParentDimIdx), std::move(parentRanges))));
    4994         574 :         newAr->SetSelf(newAr);
    4995         574 :         return newAr;
    4996             :     }
    4997             : 
    4998          55 :     bool IsWritable() const override
    4999             :     {
    5000          55 :         return m_poParent->IsWritable();
    5001             :     }
    5002             : 
    5003         983 :     const std::string &GetFilename() const override
    5004             :     {
    5005         983 :         return m_poParent->GetFilename();
    5006             :     }
    5007             : 
    5008             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5009        3646 :     GetDimensions() const override
    5010             :     {
    5011        3646 :         return m_dims;
    5012             :     }
    5013             : 
    5014        1383 :     const GDALExtendedDataType &GetDataType() const override
    5015             :     {
    5016        1383 :         return m_poParent->GetDataType();
    5017             :     }
    5018             : 
    5019           2 :     const std::string &GetUnit() const override
    5020             :     {
    5021           2 :         return m_poParent->GetUnit();
    5022             :     }
    5023             : 
    5024             :     // bool SetUnit(const std::string& osUnit) override  { return
    5025             :     // m_poParent->SetUnit(osUnit); }
    5026             : 
    5027           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5028             :     {
    5029           4 :         auto poSrcSRS = m_poParent->GetSpatialRef();
    5030           2 :         if (!poSrcSRS)
    5031           1 :             return nullptr;
    5032           2 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
    5033           2 :         std::vector<int> dstMapping;
    5034           3 :         for (int srcAxis : srcMapping)
    5035             :         {
    5036           2 :             bool bFound = false;
    5037           3 :             for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
    5038             :             {
    5039           3 :                 if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
    5040           3 :                     srcAxis - 1)
    5041             :                 {
    5042           2 :                     dstMapping.push_back(static_cast<int>(i) + 1);
    5043           2 :                     bFound = true;
    5044           2 :                     break;
    5045             :                 }
    5046             :             }
    5047           2 :             if (!bFound)
    5048             :             {
    5049           0 :                 dstMapping.push_back(0);
    5050             :             }
    5051             :         }
    5052           2 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
    5053           1 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
    5054           1 :         return poClone;
    5055             :     }
    5056             : 
    5057          55 :     const void *GetRawNoDataValue() const override
    5058             :     {
    5059          55 :         return m_poParent->GetRawNoDataValue();
    5060             :     }
    5061             : 
    5062             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
    5063             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
    5064             : 
    5065           2 :     double GetOffset(bool *pbHasOffset,
    5066             :                      GDALDataType *peStorageType) const override
    5067             :     {
    5068           2 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5069             :     }
    5070             : 
    5071           2 :     double GetScale(bool *pbHasScale,
    5072             :                     GDALDataType *peStorageType) const override
    5073             :     {
    5074           2 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5075             :     }
    5076             : 
    5077             :     // bool SetOffset(double dfOffset) override { return
    5078             :     // m_poParent->SetOffset(dfOffset); }
    5079             : 
    5080             :     // bool SetScale(double dfScale) override { return
    5081             :     // m_poParent->SetScale(dfScale); }
    5082             : 
    5083         197 :     std::vector<GUInt64> GetBlockSize() const override
    5084             :     {
    5085         197 :         std::vector<GUInt64> ret(GetDimensionCount());
    5086         394 :         const auto parentBlockSize(m_poParent->GetBlockSize());
    5087         595 :         for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
    5088             :         {
    5089         398 :             const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
    5090         398 :             if (iOldAxis != static_cast<size_t>(-1))
    5091             :             {
    5092         398 :                 ret[i] = parentBlockSize[iOldAxis];
    5093             :             }
    5094             :         }
    5095         394 :         return ret;
    5096             :     }
    5097             : 
    5098             :     std::shared_ptr<GDALAttribute>
    5099           6 :     GetAttribute(const std::string &osName) const override
    5100             :     {
    5101           6 :         return m_poParent->GetAttribute(osName);
    5102             :     }
    5103             : 
    5104             :     std::vector<std::shared_ptr<GDALAttribute>>
    5105          24 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    5106             :     {
    5107          24 :         return m_poParent->GetAttributes(papszOptions);
    5108             :     }
    5109             : };
    5110             : 
    5111             : /************************************************************************/
    5112             : /*                        PrepareParentArrays()                         */
    5113             : /************************************************************************/
    5114             : 
    5115         475 : void GDALSlicedMDArray::PrepareParentArrays(
    5116             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    5117             :     const GPtrDiff_t *bufferStride) const
    5118             : {
    5119         475 :     const size_t nParentDimCount = m_parentRanges.size();
    5120        1481 :     for (size_t i = 0; i < nParentDimCount; i++)
    5121             :     {
    5122             :         // For dimensions in parent that have no existence in sliced array
    5123        1006 :         m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
    5124             :     }
    5125             : 
    5126        1250 :     for (size_t i = 0; i < m_dims.size(); i++)
    5127             :     {
    5128         775 :         const auto iParent = m_mapDimIdxToParentDimIdx[i];
    5129         775 :         if (iParent != static_cast<size_t>(-1))
    5130             :         {
    5131         773 :             m_parentStart[iParent] =
    5132         773 :                 m_parentRanges[iParent].m_nIncr >= 0
    5133         773 :                     ? m_parentRanges[iParent].m_nStartIdx +
    5134         744 :                           arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
    5135          29 :                     : m_parentRanges[iParent].m_nStartIdx -
    5136          58 :                           arrayStartIdx[i] *
    5137          29 :                               static_cast<GUInt64>(
    5138          29 :                                   -m_parentRanges[iParent].m_nIncr);
    5139         773 :             m_parentCount[iParent] = count[i];
    5140         773 :             if (arrayStep)
    5141             :             {
    5142         772 :                 m_parentStep[iParent] =
    5143         772 :                     count[i] == 1 ? 1 :
    5144             :                                   // other checks should have ensured this does
    5145             :                         // not overflow
    5146         586 :                         arrayStep[i] * m_parentRanges[iParent].m_nIncr;
    5147             :             }
    5148         773 :             if (bufferStride)
    5149             :             {
    5150         772 :                 m_parentStride[iParent] = bufferStride[i];
    5151             :             }
    5152             :         }
    5153             :     }
    5154         475 : }
    5155             : 
    5156             : /************************************************************************/
    5157             : /*                             IRead()                                  */
    5158             : /************************************************************************/
    5159             : 
    5160         442 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5161             :                               const GInt64 *arrayStep,
    5162             :                               const GPtrDiff_t *bufferStride,
    5163             :                               const GDALExtendedDataType &bufferDataType,
    5164             :                               void *pDstBuffer) const
    5165             : {
    5166         442 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5167         884 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
    5168         442 :                             m_parentStep.data(), m_parentStride.data(),
    5169         442 :                             bufferDataType, pDstBuffer);
    5170             : }
    5171             : 
    5172             : /************************************************************************/
    5173             : /*                             IWrite()                                  */
    5174             : /************************************************************************/
    5175             : 
    5176          32 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
    5177             :                                const size_t *count, const GInt64 *arrayStep,
    5178             :                                const GPtrDiff_t *bufferStride,
    5179             :                                const GDALExtendedDataType &bufferDataType,
    5180             :                                const void *pSrcBuffer)
    5181             : {
    5182          32 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5183          64 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
    5184          32 :                              m_parentStep.data(), m_parentStride.data(),
    5185          32 :                              bufferDataType, pSrcBuffer);
    5186             : }
    5187             : 
    5188             : /************************************************************************/
    5189             : /*                             IAdviseRead()                            */
    5190             : /************************************************************************/
    5191             : 
    5192           1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
    5193             :                                     const size_t *count,
    5194             :                                     CSLConstList papszOptions) const
    5195             : {
    5196           1 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
    5197           1 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
    5198           1 :                                   papszOptions);
    5199             : }
    5200             : 
    5201             : /************************************************************************/
    5202             : /*                        CreateSlicedArray()                           */
    5203             : /************************************************************************/
    5204             : 
    5205             : static std::shared_ptr<GDALMDArray>
    5206         592 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
    5207             :                   const std::string &viewExpr, const std::string &activeSlice,
    5208             :                   bool bRenameDimensions,
    5209             :                   std::vector<GDALMDArray::ViewSpec> &viewSpecs)
    5210             : {
    5211         592 :     const auto &srcDims(self->GetDimensions());
    5212         592 :     if (srcDims.empty())
    5213             :     {
    5214           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
    5215           2 :         return nullptr;
    5216             :     }
    5217             : 
    5218        1180 :     CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
    5219         590 :     const auto nTokens = static_cast<size_t>(aosTokens.size());
    5220             : 
    5221        1180 :     std::vector<std::shared_ptr<GDALDimension>> newDims;
    5222        1180 :     std::vector<size_t> mapDimIdxToParentDimIdx;
    5223        1180 :     std::vector<GDALSlicedMDArray::Range> parentRanges;
    5224         590 :     newDims.reserve(nTokens);
    5225         590 :     mapDimIdxToParentDimIdx.reserve(nTokens);
    5226         590 :     parentRanges.reserve(nTokens);
    5227             : 
    5228         590 :     bool bGotEllipsis = false;
    5229         590 :     size_t nCurSrcDim = 0;
    5230        1744 :     for (size_t i = 0; i < nTokens; i++)
    5231             :     {
    5232        1170 :         const char *pszIdxSpec = aosTokens[i];
    5233        1170 :         if (EQUAL(pszIdxSpec, "..."))
    5234             :         {
    5235          37 :             if (bGotEllipsis)
    5236             :             {
    5237           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5238             :                          "Only one single ellipsis is supported");
    5239           2 :                 return nullptr;
    5240             :             }
    5241          35 :             bGotEllipsis = true;
    5242          35 :             const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
    5243          77 :             for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
    5244             :             {
    5245          42 :                 parentRanges.emplace_back(0, 1);
    5246          42 :                 newDims.push_back(srcDims[nCurSrcDim]);
    5247          42 :                 mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5248             :             }
    5249          35 :             continue;
    5250             :         }
    5251        1133 :         else if (EQUAL(pszIdxSpec, "newaxis") ||
    5252        1130 :                  EQUAL(pszIdxSpec, "np.newaxis"))
    5253             :         {
    5254           3 :             newDims.push_back(std::make_shared<GDALDimension>(
    5255           6 :                 std::string(), "newaxis", std::string(), std::string(), 1));
    5256           3 :             mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
    5257           3 :             continue;
    5258             :         }
    5259        1130 :         else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
    5260             :         {
    5261         323 :             if (nCurSrcDim >= srcDims.size())
    5262             :             {
    5263           2 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
    5264             :                          activeSlice.c_str());
    5265           7 :                 return nullptr;
    5266             :             }
    5267             : 
    5268         321 :             auto nVal = CPLAtoGIntBig(pszIdxSpec);
    5269         321 :             GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
    5270         321 :             if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
    5271         317 :                 (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
    5272             :             {
    5273           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5274             :                          "Index " CPL_FRMT_GIB " is out of bounds", nVal);
    5275           5 :                 return nullptr;
    5276             :             }
    5277         316 :             if (nVal < 0)
    5278           0 :                 nVal += nDimSize;
    5279         316 :             parentRanges.emplace_back(nVal, 0);
    5280             :         }
    5281             :         else
    5282             :         {
    5283         807 :             if (nCurSrcDim >= srcDims.size())
    5284             :             {
    5285           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
    5286             :                          activeSlice.c_str());
    5287           7 :                 return nullptr;
    5288             :             }
    5289             : 
    5290             :             CPLStringList aosRangeTokens(
    5291         806 :                 CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
    5292         806 :             int nRangeTokens = aosRangeTokens.size();
    5293         806 :             if (nRangeTokens > 3)
    5294             :             {
    5295           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
    5296             :                          pszIdxSpec);
    5297           1 :                 return nullptr;
    5298             :             }
    5299         805 :             if (nRangeTokens <= 1)
    5300             :             {
    5301           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
    5302             :                          pszIdxSpec);
    5303           1 :                 return nullptr;
    5304             :             }
    5305         804 :             const char *pszStart = aosRangeTokens[0];
    5306         804 :             const char *pszEnd = aosRangeTokens[1];
    5307         804 :             const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
    5308         804 :             GDALSlicedMDArray::Range range;
    5309         804 :             const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
    5310         804 :             range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
    5311        1607 :             if (range.m_nIncr == 0 ||
    5312         803 :                 range.m_nIncr == std::numeric_limits<GInt64>::min())
    5313             :             {
    5314           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
    5315           1 :                 return nullptr;
    5316             :             }
    5317         803 :             auto startIdx(CPLAtoGIntBig(pszStart));
    5318         803 :             if (startIdx < 0)
    5319             :             {
    5320           0 :                 if (nDimSize < static_cast<GUInt64>(-startIdx))
    5321           0 :                     startIdx = 0;
    5322             :                 else
    5323           0 :                     startIdx = nDimSize + startIdx;
    5324             :             }
    5325         803 :             const bool bPosIncr = range.m_nIncr > 0;
    5326         803 :             range.m_nStartIdx = startIdx;
    5327        1606 :             range.m_nStartIdx = EQUAL(pszStart, "")
    5328         803 :                                     ? (bPosIncr ? 0 : nDimSize - 1)
    5329             :                                     : range.m_nStartIdx;
    5330         803 :             if (range.m_nStartIdx >= nDimSize - 1)
    5331         185 :                 range.m_nStartIdx = nDimSize - 1;
    5332         803 :             auto endIdx(CPLAtoGIntBig(pszEnd));
    5333         803 :             if (endIdx < 0)
    5334             :             {
    5335           1 :                 const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
    5336           1 :                 if (nDimSize < positiveEndIdx)
    5337           0 :                     endIdx = 0;
    5338             :                 else
    5339           1 :                     endIdx = nDimSize - positiveEndIdx;
    5340             :             }
    5341         803 :             GUInt64 nEndIdx = endIdx;
    5342         803 :             nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
    5343         803 :             if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
    5344         801 :                 (!bPosIncr && range.m_nStartIdx <= nEndIdx))
    5345             :             {
    5346           3 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5347             :                          "Output dimension of size 0 is not allowed");
    5348           3 :                 return nullptr;
    5349             :             }
    5350         800 :             int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
    5351         800 :             const auto nAbsIncr = std::abs(range.m_nIncr);
    5352         800 :             const GUInt64 newSize =
    5353             :                 bPosIncr
    5354         833 :                     ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
    5355          33 :                     : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
    5356        1322 :             if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
    5357         522 :                 newSize == srcDims[nCurSrcDim]->GetSize())
    5358             :             {
    5359         153 :                 newDims.push_back(srcDims[nCurSrcDim]);
    5360             :             }
    5361             :             else
    5362             :             {
    5363         647 :                 std::string osNewDimName(srcDims[nCurSrcDim]->GetName());
    5364         647 :                 if (bRenameDimensions)
    5365             :                 {
    5366             :                     osNewDimName =
    5367        1210 :                         "subset_" + srcDims[nCurSrcDim]->GetName() +
    5368             :                         CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
    5369             :                                    "_" CPL_FRMT_GUIB,
    5370         605 :                                    static_cast<GUIntBig>(range.m_nStartIdx),
    5371         605 :                                    static_cast<GIntBig>(range.m_nIncr),
    5372         605 :                                    static_cast<GUIntBig>(newSize));
    5373             :                 }
    5374         647 :                 newDims.push_back(std::make_shared<GDALDimension>(
    5375        1294 :                     std::string(), osNewDimName, srcDims[nCurSrcDim]->GetType(),
    5376        1294 :                     range.m_nIncr > 0 ? srcDims[nCurSrcDim]->GetDirection()
    5377             :                                       : std::string(),
    5378             :                     newSize));
    5379             :             }
    5380         800 :             mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5381         800 :             parentRanges.emplace_back(range);
    5382             :         }
    5383             : 
    5384        1116 :         nCurSrcDim++;
    5385             :     }
    5386         647 :     for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
    5387             :     {
    5388          73 :         parentRanges.emplace_back(0, 1);
    5389          73 :         newDims.push_back(srcDims[nCurSrcDim]);
    5390          73 :         mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5391             :     }
    5392             : 
    5393         574 :     GDALMDArray::ViewSpec viewSpec;
    5394         574 :     viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
    5395         574 :     viewSpec.m_parentRanges = parentRanges;
    5396         574 :     viewSpecs.emplace_back(std::move(viewSpec));
    5397             : 
    5398        1148 :     return GDALSlicedMDArray::Create(self, viewExpr, std::move(newDims),
    5399         574 :                                      std::move(mapDimIdxToParentDimIdx),
    5400        1148 :                                      std::move(parentRanges));
    5401             : }
    5402             : 
    5403             : /************************************************************************/
    5404             : /*                       GDALExtractFieldMDArray                        */
    5405             : /************************************************************************/
    5406             : 
    5407             : class GDALExtractFieldMDArray final : public GDALPamMDArray
    5408             : {
    5409             :   private:
    5410             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5411             :     GDALExtendedDataType m_dt;
    5412             :     std::string m_srcCompName;
    5413             :     mutable std::vector<GByte> m_pabyNoData{};
    5414             : 
    5415             :   protected:
    5416          62 :     GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
    5417             :                             const std::string &fieldName,
    5418             :                             const std::unique_ptr<GDALEDTComponent> &srcComp)
    5419         248 :         : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
    5420         124 :                                                  " of " +
    5421          62 :                                                  poParent->GetFullName()),
    5422             :           GDALPamMDArray(
    5423         124 :               std::string(),
    5424         124 :               "Extract field " + fieldName + " of " + poParent->GetFullName(),
    5425         124 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
    5426             :           m_poParent(poParent), m_dt(srcComp->GetType()),
    5427         310 :           m_srcCompName(srcComp->GetName())
    5428             :     {
    5429          62 :         m_pabyNoData.resize(m_dt.GetSize());
    5430          62 :     }
    5431             : 
    5432             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5433             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5434             :                const GDALExtendedDataType &bufferDataType,
    5435             :                void *pDstBuffer) const override;
    5436             : 
    5437           1 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5438             :                      CSLConstList papszOptions) const override
    5439             :     {
    5440           1 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
    5441             :     }
    5442             : 
    5443             :   public:
    5444             :     static std::shared_ptr<GDALExtractFieldMDArray>
    5445          62 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5446             :            const std::string &fieldName,
    5447             :            const std::unique_ptr<GDALEDTComponent> &srcComp)
    5448             :     {
    5449             :         auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
    5450          62 :             new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
    5451          62 :         newAr->SetSelf(newAr);
    5452          62 :         return newAr;
    5453             :     }
    5454             : 
    5455         124 :     ~GDALExtractFieldMDArray()
    5456          62 :     {
    5457          62 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
    5458         124 :     }
    5459             : 
    5460          40 :     bool IsWritable() const override
    5461             :     {
    5462          40 :         return m_poParent->IsWritable();
    5463             :     }
    5464             : 
    5465         204 :     const std::string &GetFilename() const override
    5466             :     {
    5467         204 :         return m_poParent->GetFilename();
    5468             :     }
    5469             : 
    5470             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5471         300 :     GetDimensions() const override
    5472             :     {
    5473         300 :         return m_poParent->GetDimensions();
    5474             :     }
    5475             : 
    5476         245 :     const GDALExtendedDataType &GetDataType() const override
    5477             :     {
    5478         245 :         return m_dt;
    5479             :     }
    5480             : 
    5481           2 :     const std::string &GetUnit() const override
    5482             :     {
    5483           2 :         return m_poParent->GetUnit();
    5484             :     }
    5485             : 
    5486           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5487             :     {
    5488           2 :         return m_poParent->GetSpatialRef();
    5489             :     }
    5490             : 
    5491          56 :     const void *GetRawNoDataValue() const override
    5492             :     {
    5493          56 :         const void *parentNoData = m_poParent->GetRawNoDataValue();
    5494          56 :         if (parentNoData == nullptr)
    5495           1 :             return nullptr;
    5496             : 
    5497          55 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
    5498          55 :         memset(&m_pabyNoData[0], 0, m_dt.GetSize());
    5499             : 
    5500         110 :         std::vector<std::unique_ptr<GDALEDTComponent>> comps;
    5501         110 :         comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
    5502         110 :             new GDALEDTComponent(m_srcCompName, 0, m_dt)));
    5503          55 :         auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
    5504         165 :                                                 std::move(comps)));
    5505             : 
    5506          55 :         GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
    5507          55 :                                         &m_pabyNoData[0], tmpDT);
    5508             : 
    5509          55 :         return &m_pabyNoData[0];
    5510             :     }
    5511             : 
    5512           2 :     double GetOffset(bool *pbHasOffset,
    5513             :                      GDALDataType *peStorageType) const override
    5514             :     {
    5515           2 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5516             :     }
    5517             : 
    5518           2 :     double GetScale(bool *pbHasScale,
    5519             :                     GDALDataType *peStorageType) const override
    5520             :     {
    5521           2 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5522             :     }
    5523             : 
    5524          41 :     std::vector<GUInt64> GetBlockSize() const override
    5525             :     {
    5526          41 :         return m_poParent->GetBlockSize();
    5527             :     }
    5528             : };
    5529             : 
    5530             : /************************************************************************/
    5531             : /*                             IRead()                                  */
    5532             : /************************************************************************/
    5533             : 
    5534          46 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
    5535             :                                     const size_t *count,
    5536             :                                     const GInt64 *arrayStep,
    5537             :                                     const GPtrDiff_t *bufferStride,
    5538             :                                     const GDALExtendedDataType &bufferDataType,
    5539             :                                     void *pDstBuffer) const
    5540             : {
    5541          92 :     std::vector<std::unique_ptr<GDALEDTComponent>> comps;
    5542          92 :     comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
    5543          92 :         new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
    5544             :     auto tmpDT(GDALExtendedDataType::Create(
    5545          92 :         std::string(), bufferDataType.GetSize(), std::move(comps)));
    5546             : 
    5547          46 :     return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
    5548          92 :                             tmpDT, pDstBuffer);
    5549             : }
    5550             : 
    5551             : /************************************************************************/
    5552             : /*                      CreateFieldNameExtractArray()                   */
    5553             : /************************************************************************/
    5554             : 
    5555             : static std::shared_ptr<GDALMDArray>
    5556          63 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
    5557             :                             const std::string &fieldName)
    5558             : {
    5559          63 :     CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
    5560          63 :     const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
    5561         125 :     for (const auto &comp : self->GetDataType().GetComponents())
    5562             :     {
    5563         124 :         if (comp->GetName() == fieldName)
    5564             :         {
    5565          62 :             srcComp = &comp;
    5566          62 :             break;
    5567             :         }
    5568             :     }
    5569          63 :     if (srcComp == nullptr)
    5570             :     {
    5571           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
    5572             :                  fieldName.c_str());
    5573           1 :         return nullptr;
    5574             :     }
    5575          62 :     return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
    5576             : }
    5577             : 
    5578             : /************************************************************************/
    5579             : /*                             GetView()                                */
    5580             : /************************************************************************/
    5581             : 
    5582             : // clang-format off
    5583             : /** Return a view of the array using slicing or field access.
    5584             :  *
    5585             :  * The slice expression uses the same syntax as NumPy basic slicing and
    5586             :  * indexing. See
    5587             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
    5588             :  * Or it can use field access by name. See
    5589             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
    5590             :  *
    5591             :  * Multiple [] bracket elements can be concatenated, with a slice expression
    5592             :  * or field name inside each.
    5593             :  *
    5594             :  * For basic slicing and indexing, inside each [] bracket element, a list of
    5595             :  * indexes that apply to successive source dimensions, can be specified, using
    5596             :  * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
    5597             :  * or newaxis, using a comma separator.
    5598             :  *
    5599             :  * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
    5600             :  * <ul>
    5601             :  * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
    5602             :  *     at index 1 in the first dimension, and index 2 in the second dimension
    5603             :  *     from the source array. That is 5</li>
    5604             :  * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
    5605             :  * implemented internally doing this intermediate slicing approach.</li>
    5606             :  * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
    5607             :  * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
    5608             :  *     first dimension. That is [4,5,6,7].</li>
    5609             :  * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
    5610             :  *     second dimension. That is [2,6].</li>
    5611             :  * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
    5612             :  * the second dimension. That is [[2],[6]].</li>
    5613             :  * <li>GetView("[::,2]"): Same as
    5614             :  * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
    5615             :  * ellipsis only expands to one dimension here.</li>
    5616             :  * <li>GetView("[:,::2]"):
    5617             :  * returns a 2-dimensional array, with even-indexed elements of the second
    5618             :  * dimension. That is [[0,2],[4,6]].</li>
    5619             :  * <li>GetView("[:,1::2]"): returns a
    5620             :  * 2-dimensional array, with odd-indexed elements of the second dimension. That
    5621             :  * is [[1,3],[5,7]].</li>
    5622             :  * <li>GetView("[:,1:3:]"): returns a 2-dimensional
    5623             :  * array, with elements of the second dimension with index in the range [1,3[.
    5624             :  * That is [[1,2],[5,6]].</li>
    5625             :  * <li>GetView("[::-1,:]"): returns a 2-dimensional
    5626             :  * array, with the values in first dimension reversed. That is
    5627             :  * [[4,5,6,7],[0,1,2,3]].</li>
    5628             :  * <li>GetView("[newaxis,...]"): returns a
    5629             :  * 3-dimensional array, with an additional dimension of size 1 put at the
    5630             :  * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
    5631             :  * </ul>
    5632             :  *
    5633             :  * One difference with NumPy behavior is that ranges that would result in
    5634             :  * zero elements are not allowed (dimensions of size 0 not being allowed in the
    5635             :  * GDAL multidimensional model).
    5636             :  *
    5637             :  * For field access, the syntax to use is ["field_name"] or ['field_name'].
    5638             :  * Multiple field specification is not supported currently.
    5639             :  *
    5640             :  * Both type of access can be combined, e.g. GetView("[1]['field_name']")
    5641             :  *
    5642             :  * \note When using the GDAL Python bindings, natural Python syntax can be
    5643             :  * used. That is ar[0,::,1]["foo"] will be internally translated to
    5644             :  * ar.GetView("[0,::,1]['foo']")
    5645             :  * \note When using the C++ API and integer indexing only, you may use the
    5646             :  * at(idx0, idx1, ...) method.
    5647             :  *
    5648             :  * The returned array holds a reference to the original one, and thus is
    5649             :  * a view of it (not a copy). If the content of the original array changes,
    5650             :  * the content of the view array too. When using basic slicing and indexing,
    5651             :  * the view can be written if the underlying array is writable.
    5652             :  *
    5653             :  * This is the same as the C function GDALMDArrayGetView()
    5654             :  *
    5655             :  * @param viewExpr Expression expressing basic slicing and indexing, or field
    5656             :  * access.
    5657             :  * @return a new array, that holds a reference to the original one, and thus is
    5658             :  * a view of it (not a copy), or nullptr in case of error.
    5659             :  */
    5660             : // clang-format on
    5661             : 
    5662             : std::shared_ptr<GDALMDArray>
    5663         598 : GDALMDArray::GetView(const std::string &viewExpr) const
    5664             : {
    5665        1196 :     std::vector<ViewSpec> viewSpecs;
    5666        1196 :     return GetView(viewExpr, true, viewSpecs);
    5667             : }
    5668             : 
    5669             : //! @cond Doxygen_Suppress
    5670             : std::shared_ptr<GDALMDArray>
    5671         661 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
    5672             :                      std::vector<ViewSpec> &viewSpecs) const
    5673             : {
    5674        1322 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    5675         661 :     if (!self)
    5676             :     {
    5677           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    5678             :                  "Driver implementation issue: m_pSelf not set !");
    5679           1 :         return nullptr;
    5680             :     }
    5681         660 :     std::string curExpr(viewExpr);
    5682             :     while (true)
    5683             :     {
    5684         663 :         if (curExpr.empty() || curExpr[0] != '[')
    5685             :         {
    5686           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    5687             :                      "Slice string should start with ['");
    5688         660 :             return nullptr;
    5689             :         }
    5690             : 
    5691         661 :         std::string fieldName;
    5692             :         size_t endExpr;
    5693         661 :         if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
    5694             :         {
    5695          67 :             if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
    5696             :             {
    5697           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5698             :                          "Field access not allowed on non-compound data type");
    5699           2 :                 return nullptr;
    5700             :             }
    5701          65 :             size_t idx = 2;
    5702         572 :             for (; idx < curExpr.size(); idx++)
    5703             :             {
    5704         571 :                 const char ch = curExpr[idx];
    5705         571 :                 if (ch == curExpr[1])
    5706          64 :                     break;
    5707         507 :                 if (ch == '\\' && idx + 1 < curExpr.size())
    5708             :                 {
    5709           1 :                     fieldName += curExpr[idx + 1];
    5710           1 :                     idx++;
    5711             :                 }
    5712             :                 else
    5713             :                 {
    5714         506 :                     fieldName += ch;
    5715             :                 }
    5716             :             }
    5717          65 :             if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
    5718             :             {
    5719           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5720             :                          "Invalid field access specification");
    5721           2 :                 return nullptr;
    5722             :             }
    5723          63 :             endExpr = idx + 1;
    5724             :         }
    5725             :         else
    5726             :         {
    5727         594 :             endExpr = curExpr.find(']');
    5728             :         }
    5729         657 :         if (endExpr == std::string::npos)
    5730             :         {
    5731           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
    5732           1 :             return nullptr;
    5733             :         }
    5734         656 :         if (endExpr == 1)
    5735             :         {
    5736           1 :             CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
    5737           1 :             return nullptr;
    5738             :         }
    5739         655 :         std::string activeSlice(curExpr.substr(1, endExpr - 1));
    5740             : 
    5741         655 :         if (!fieldName.empty())
    5742             :         {
    5743         126 :             ViewSpec viewSpec;
    5744          63 :             viewSpec.m_osFieldName = fieldName;
    5745          63 :             viewSpecs.emplace_back(std::move(viewSpec));
    5746             :         }
    5747             : 
    5748         655 :         auto newArray = !fieldName.empty()
    5749             :                             ? CreateFieldNameExtractArray(self, fieldName)
    5750             :                             : CreateSlicedArray(self, viewExpr, activeSlice,
    5751         655 :                                                 bRenameDimensions, viewSpecs);
    5752             : 
    5753         655 :         if (endExpr == curExpr.size() - 1)
    5754             :         {
    5755         652 :             return newArray;
    5756             :         }
    5757           3 :         self = std::move(newArray);
    5758           3 :         curExpr = curExpr.substr(endExpr + 1);
    5759           3 :     }
    5760             : }
    5761             : 
    5762             : //! @endcond
    5763             : 
    5764             : std::shared_ptr<GDALMDArray>
    5765          19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
    5766             : {
    5767          19 :     std::string osExpr("[");
    5768          19 :     bool bFirst = true;
    5769          45 :     for (const auto &idx : indices)
    5770             :     {
    5771          26 :         if (!bFirst)
    5772           7 :             osExpr += ',';
    5773          26 :         bFirst = false;
    5774          26 :         osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
    5775             :     }
    5776          57 :     return GetView(osExpr + ']');
    5777             : }
    5778             : 
    5779             : /************************************************************************/
    5780             : /*                            operator[]                                */
    5781             : /************************************************************************/
    5782             : 
    5783             : /** Return a view of the array using field access
    5784             :  *
    5785             :  * Equivalent of GetView("['fieldName']")
    5786             :  *
    5787             :  * \note When operationg on a shared_ptr, use (*array)["fieldName"] syntax.
    5788             :  */
    5789             : std::shared_ptr<GDALMDArray>
    5790           2 : GDALMDArray::operator[](const std::string &fieldName) const
    5791             : {
    5792           2 :     return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
    5793           4 :                                             .replaceAll('\\', "\\\\")
    5794           4 :                                             .replaceAll('\'', "\\\'")
    5795           6 :                                             .c_str()));
    5796             : }
    5797             : 
    5798             : /************************************************************************/
    5799             : /*                      GDALMDArrayTransposed                           */
    5800             : /************************************************************************/
    5801             : 
    5802             : class GDALMDArrayTransposed final : public GDALPamMDArray
    5803             : {
    5804             :   private:
    5805             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5806             :     std::vector<int> m_anMapNewAxisToOldAxis{};
    5807             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    5808             : 
    5809             :     mutable std::vector<GUInt64> m_parentStart;
    5810             :     mutable std::vector<size_t> m_parentCount;
    5811             :     mutable std::vector<GInt64> m_parentStep;
    5812             :     mutable std::vector<GPtrDiff_t> m_parentStride;
    5813             : 
    5814             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
    5815             :                              const GInt64 *arrayStep,
    5816             :                              const GPtrDiff_t *bufferStride) const;
    5817             : 
    5818             :     static std::string
    5819          84 :     MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
    5820             :     {
    5821          84 :         std::string ret;
    5822          84 :         ret += '[';
    5823         312 :         for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
    5824             :         {
    5825         228 :             if (i > 0)
    5826         144 :                 ret += ',';
    5827         228 :             ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
    5828             :         }
    5829          84 :         ret += ']';
    5830          84 :         return ret;
    5831             :     }
    5832             : 
    5833             :   protected:
    5834          42 :     GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
    5835             :                           const std::vector<int> &anMapNewAxisToOldAxis,
    5836             :                           std::vector<std::shared_ptr<GDALDimension>> &&dims)
    5837          84 :         : GDALAbstractMDArray(std::string(),
    5838          84 :                               "Transposed view of " + poParent->GetFullName() +
    5839          84 :                                   " along " +
    5840          42 :                                   MappingToStr(anMapNewAxisToOldAxis)),
    5841          84 :           GDALPamMDArray(std::string(),
    5842          84 :                          "Transposed view of " + poParent->GetFullName() +
    5843         168 :                              " along " + MappingToStr(anMapNewAxisToOldAxis),
    5844          84 :                          GDALPamMultiDim::GetPAM(poParent),
    5845             :                          poParent->GetContext()),
    5846          42 :           m_poParent(std::move(poParent)),
    5847             :           m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
    5848          42 :           m_dims(std::move(dims)),
    5849          42 :           m_parentStart(m_poParent->GetDimensionCount()),
    5850          42 :           m_parentCount(m_poParent->GetDimensionCount()),
    5851          42 :           m_parentStep(m_poParent->GetDimensionCount()),
    5852         336 :           m_parentStride(m_poParent->GetDimensionCount())
    5853             :     {
    5854          42 :     }
    5855             : 
    5856             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5857             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5858             :                const GDALExtendedDataType &bufferDataType,
    5859             :                void *pDstBuffer) const override;
    5860             : 
    5861             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    5862             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5863             :                 const GDALExtendedDataType &bufferDataType,
    5864             :                 const void *pSrcBuffer) override;
    5865             : 
    5866             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5867             :                      CSLConstList papszOptions) const override;
    5868             : 
    5869             :   public:
    5870             :     static std::shared_ptr<GDALMDArrayTransposed>
    5871          42 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5872             :            const std::vector<int> &anMapNewAxisToOldAxis)
    5873             :     {
    5874          42 :         const auto &parentDims(poParent->GetDimensions());
    5875          84 :         std::vector<std::shared_ptr<GDALDimension>> dims;
    5876         156 :         for (const auto iOldAxis : anMapNewAxisToOldAxis)
    5877             :         {
    5878         114 :             if (iOldAxis < 0)
    5879             :             {
    5880           1 :                 dims.push_back(std::make_shared<GDALDimension>(
    5881           2 :                     std::string(), "newaxis", std::string(), std::string(), 1));
    5882             :             }
    5883             :             else
    5884             :             {
    5885         113 :                 dims.emplace_back(parentDims[iOldAxis]);
    5886             :             }
    5887             :         }
    5888             : 
    5889             :         auto newAr(
    5890             :             std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
    5891          42 :                 poParent, anMapNewAxisToOldAxis, std::move(dims))));
    5892          42 :         newAr->SetSelf(newAr);
    5893          84 :         return newAr;
    5894             :     }
    5895             : 
    5896           1 :     bool IsWritable() const override
    5897             :     {
    5898           1 :         return m_poParent->IsWritable();
    5899             :     }
    5900             : 
    5901          84 :     const std::string &GetFilename() const override
    5902             :     {
    5903          84 :         return m_poParent->GetFilename();
    5904             :     }
    5905             : 
    5906             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5907         358 :     GetDimensions() const override
    5908             :     {
    5909         358 :         return m_dims;
    5910             :     }
    5911             : 
    5912         141 :     const GDALExtendedDataType &GetDataType() const override
    5913             :     {
    5914         141 :         return m_poParent->GetDataType();
    5915             :     }
    5916             : 
    5917           4 :     const std::string &GetUnit() const override
    5918             :     {
    5919           4 :         return m_poParent->GetUnit();
    5920             :     }
    5921             : 
    5922           5 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5923             :     {
    5924          10 :         auto poSrcSRS = m_poParent->GetSpatialRef();
    5925           5 :         if (!poSrcSRS)
    5926           2 :             return nullptr;
    5927           6 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
    5928           6 :         std::vector<int> dstMapping;
    5929           9 :         for (int srcAxis : srcMapping)
    5930             :         {
    5931           6 :             bool bFound = false;
    5932          14 :             for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
    5933             :             {
    5934          14 :                 if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
    5935             :                 {
    5936           6 :                     dstMapping.push_back(static_cast<int>(i) + 1);
    5937           6 :                     bFound = true;
    5938           6 :                     break;
    5939             :                 }
    5940             :             }
    5941           6 :             if (!bFound)
    5942             :             {
    5943           0 :                 dstMapping.push_back(0);
    5944             :             }
    5945             :         }
    5946           6 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
    5947           3 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
    5948           3 :         return poClone;
    5949             :     }
    5950             : 
    5951           4 :     const void *GetRawNoDataValue() const override
    5952             :     {
    5953           4 :         return m_poParent->GetRawNoDataValue();
    5954             :     }
    5955             : 
    5956             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
    5957             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
    5958             : 
    5959           4 :     double GetOffset(bool *pbHasOffset,
    5960             :                      GDALDataType *peStorageType) const override
    5961             :     {
    5962           4 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5963             :     }
    5964             : 
    5965           4 :     double GetScale(bool *pbHasScale,
    5966             :                     GDALDataType *peStorageType) const override
    5967             :     {
    5968           4 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5969             :     }
    5970             : 
    5971             :     // bool SetOffset(double dfOffset) override { return
    5972             :     // m_poParent->SetOffset(dfOffset); }
    5973             : 
    5974             :     // bool SetScale(double dfScale) override { return
    5975             :     // m_poParent->SetScale(dfScale); }
    5976             : 
    5977           3 :     std::vector<GUInt64> GetBlockSize() const override
    5978             :     {
    5979           3 :         std::vector<GUInt64> ret(GetDimensionCount());
    5980           6 :         const auto parentBlockSize(m_poParent->GetBlockSize());
    5981          11 :         for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
    5982             :         {
    5983           8 :             const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
    5984           8 :             if (iOldAxis >= 0)
    5985             :             {
    5986           7 :                 ret[i] = parentBlockSize[iOldAxis];
    5987             :             }
    5988             :         }
    5989           6 :         return ret;
    5990             :     }
    5991             : 
    5992             :     std::shared_ptr<GDALAttribute>
    5993           1 :     GetAttribute(const std::string &osName) const override
    5994             :     {
    5995           1 :         return m_poParent->GetAttribute(osName);
    5996             :     }
    5997             : 
    5998             :     std::vector<std::shared_ptr<GDALAttribute>>
    5999           6 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    6000             :     {
    6001           6 :         return m_poParent->GetAttributes(papszOptions);
    6002             :     }
    6003             : };
    6004             : 
    6005             : /************************************************************************/
    6006             : /*                         PrepareParentArrays()                        */
    6007             : /************************************************************************/
    6008             : 
    6009          47 : void GDALMDArrayTransposed::PrepareParentArrays(
    6010             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    6011             :     const GPtrDiff_t *bufferStride) const
    6012             : {
    6013         176 :     for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
    6014             :     {
    6015         129 :         const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
    6016         129 :         if (iOldAxis >= 0)
    6017             :         {
    6018         128 :             m_parentStart[iOldAxis] = arrayStartIdx[i];
    6019         128 :             m_parentCount[iOldAxis] = count[i];
    6020         128 :             if (arrayStep)  // only null when called from IAdviseRead()
    6021             :             {
    6022         126 :                 m_parentStep[iOldAxis] = arrayStep[i];
    6023             :             }
    6024         128 :             if (bufferStride)  // only null when called from IAdviseRead()
    6025             :             {
    6026         126 :                 m_parentStride[iOldAxis] = bufferStride[i];
    6027             :             }
    6028             :         }
    6029             :     }
    6030          47 : }
    6031             : 
    6032             : /************************************************************************/
    6033             : /*                             IRead()                                  */
    6034             : /************************************************************************/
    6035             : 
    6036          44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
    6037             :                                   const size_t *count, const GInt64 *arrayStep,
    6038             :                                   const GPtrDiff_t *bufferStride,
    6039             :                                   const GDALExtendedDataType &bufferDataType,
    6040             :                                   void *pDstBuffer) const
    6041             : {
    6042          44 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    6043          88 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
    6044          44 :                             m_parentStep.data(), m_parentStride.data(),
    6045          44 :                             bufferDataType, pDstBuffer);
    6046             : }
    6047             : 
    6048             : /************************************************************************/
    6049             : /*                            IWrite()                                  */
    6050             : /************************************************************************/
    6051             : 
    6052           2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
    6053             :                                    const size_t *count, const GInt64 *arrayStep,
    6054             :                                    const GPtrDiff_t *bufferStride,
    6055             :                                    const GDALExtendedDataType &bufferDataType,
    6056             :                                    const void *pSrcBuffer)
    6057             : {
    6058           2 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    6059           4 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
    6060           2 :                              m_parentStep.data(), m_parentStride.data(),
    6061           2 :                              bufferDataType, pSrcBuffer);
    6062             : }
    6063             : 
    6064             : /************************************************************************/
    6065             : /*                             IAdviseRead()                            */
    6066             : /************************************************************************/
    6067             : 
    6068           1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
    6069             :                                         const size_t *count,
    6070             :                                         CSLConstList papszOptions) const
    6071             : {
    6072           1 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
    6073           1 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
    6074           1 :                                   papszOptions);
    6075             : }
    6076             : 
    6077             : /************************************************************************/
    6078             : /*                           Transpose()                                */
    6079             : /************************************************************************/
    6080             : 
    6081             : /** Return a view of the array whose axis have been reordered.
    6082             :  *
    6083             :  * The anMapNewAxisToOldAxis parameter should contain all the values between 0
    6084             :  * and GetDimensionCount() - 1, and each only once.
    6085             :  * -1 can be used as a special index value to ask for the insertion of a new
    6086             :  * axis of size 1.
    6087             :  * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
    6088             :  * index of one of its dimension, it corresponds to the axis of index
    6089             :  * anMapNewAxisToOldAxis[i] from the current array.
    6090             :  *
    6091             :  * This is similar to the numpy.transpose() method
    6092             :  *
    6093             :  * The returned array holds a reference to the original one, and thus is
    6094             :  * a view of it (not a copy). If the content of the original array changes,
    6095             :  * the content of the view array too. The view can be written if the underlying
    6096             :  * array is writable.
    6097             :  *
    6098             :  * Note that I/O performance in such a transposed view might be poor.
    6099             :  *
    6100             :  * This is the same as the C function GDALMDArrayTranspose().
    6101             :  *
    6102             :  * @return a new array, that holds a reference to the original one, and thus is
    6103             :  * a view of it (not a copy), or nullptr in case of error.
    6104             :  */
    6105             : std::shared_ptr<GDALMDArray>
    6106          50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
    6107             : {
    6108         100 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    6109          50 :     if (!self)
    6110             :     {
    6111           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6112             :                  "Driver implementation issue: m_pSelf not set !");
    6113           0 :         return nullptr;
    6114             :     }
    6115          50 :     const int nDims = static_cast<int>(GetDimensionCount());
    6116         100 :     std::vector<bool> alreadyUsedOldAxis(nDims, false);
    6117          50 :     int nCountOldAxis = 0;
    6118         179 :     for (const auto iOldAxis : anMapNewAxisToOldAxis)
    6119             :     {
    6120         133 :         if (iOldAxis < -1 || iOldAxis >= nDims)
    6121             :         {
    6122           3 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
    6123           4 :             return nullptr;
    6124             :         }
    6125         130 :         if (iOldAxis >= 0)
    6126             :         {
    6127         128 :             if (alreadyUsedOldAxis[iOldAxis])
    6128             :             {
    6129           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
    6130             :                          iOldAxis);
    6131           1 :                 return nullptr;
    6132             :             }
    6133         127 :             alreadyUsedOldAxis[iOldAxis] = true;
    6134         127 :             nCountOldAxis++;
    6135             :         }
    6136             :     }
    6137          46 :     if (nCountOldAxis != nDims)
    6138             :     {
    6139           4 :         CPLError(CE_Failure, CPLE_AppDefined,
    6140             :                  "One or several original axis missing");
    6141           4 :         return nullptr;
    6142             :     }
    6143          42 :     return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
    6144             : }
    6145             : 
    6146             : /************************************************************************/
    6147             : /*                             IRead()                                  */
    6148             : /************************************************************************/
    6149             : 
    6150          16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
    6151             :                                 const size_t *count, const GInt64 *arrayStep,
    6152             :                                 const GPtrDiff_t *bufferStride,
    6153             :                                 const GDALExtendedDataType &bufferDataType,
    6154             :                                 void *pDstBuffer) const
    6155             : {
    6156          16 :     const double dfScale = m_dfScale;
    6157          16 :     const double dfOffset = m_dfOffset;
    6158          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
    6159             :     const auto dtDouble =
    6160          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
    6161          16 :     const size_t nDTSize = dtDouble.GetSize();
    6162          16 :     const bool bTempBufferNeeded = (dtDouble != bufferDataType);
    6163             : 
    6164          16 :     double adfSrcNoData[2] = {0, 0};
    6165          16 :     if (m_bHasNoData)
    6166             :     {
    6167           9 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
    6168           9 :                                         m_poParent->GetDataType(),
    6169             :                                         &adfSrcNoData[0], dtDouble);
    6170             :     }
    6171             : 
    6172          16 :     const auto nDims = GetDimensions().size();
    6173          16 :     if (nDims == 0)
    6174             :     {
    6175             :         double adfVal[2];
    6176           9 :         if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
    6177             :                               dtDouble, &adfVal[0]))
    6178             :         {
    6179           0 :             return false;
    6180             :         }
    6181           9 :         if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
    6182             :         {
    6183           6 :             adfVal[0] = adfVal[0] * dfScale + dfOffset;
    6184           6 :             if (bDTIsComplex)
    6185             :             {
    6186           2 :                 adfVal[1] = adfVal[1] * dfScale + dfOffset;
    6187             :             }
    6188           6 :             GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
    6189             :                                             bufferDataType);
    6190             :         }
    6191             :         else
    6192             :         {
    6193           3 :             GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
    6194             :                                             pDstBuffer, bufferDataType);
    6195             :         }
    6196           9 :         return true;
    6197             :     }
    6198             : 
    6199          14 :     std::vector<GPtrDiff_t> actualBufferStrideVector;
    6200           7 :     const GPtrDiff_t *actualBufferStridePtr = bufferStride;
    6201           7 :     void *pTempBuffer = pDstBuffer;
    6202           7 :     if (bTempBufferNeeded)
    6203             :     {
    6204           2 :         size_t nElts = 1;
    6205           2 :         actualBufferStrideVector.resize(nDims);
    6206           7 :         for (size_t i = 0; i < nDims; i++)
    6207           5 :             nElts *= count[i];
    6208           2 :         actualBufferStrideVector.back() = 1;
    6209           5 :         for (size_t i = nDims - 1; i > 0;)
    6210             :         {
    6211           3 :             --i;
    6212           3 :             actualBufferStrideVector[i] =
    6213           3 :                 actualBufferStrideVector[i + 1] * count[i + 1];
    6214             :         }
    6215           2 :         actualBufferStridePtr = actualBufferStrideVector.data();
    6216           2 :         pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
    6217           2 :         if (!pTempBuffer)
    6218           0 :             return false;
    6219             :     }
    6220           7 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
    6221             :                           actualBufferStridePtr, dtDouble, pTempBuffer))
    6222             :     {
    6223           0 :         if (bTempBufferNeeded)
    6224           0 :             VSIFree(pTempBuffer);
    6225           0 :         return false;
    6226             :     }
    6227             : 
    6228             :     struct Stack
    6229             :     {
    6230             :         size_t nIters = 0;
    6231             :         double *src_ptr = nullptr;
    6232             :         GByte *dst_ptr = nullptr;
    6233             :         GPtrDiff_t src_inc_offset = 0;
    6234             :         GPtrDiff_t dst_inc_offset = 0;
    6235             :     };
    6236             : 
    6237           7 :     std::vector<Stack> stack(nDims);
    6238           7 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    6239          23 :     for (size_t i = 0; i < nDims; i++)
    6240             :     {
    6241          32 :         stack[i].src_inc_offset =
    6242          16 :             actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
    6243          16 :         stack[i].dst_inc_offset =
    6244          16 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6245             :     }
    6246           7 :     stack[0].src_ptr = static_cast<double *>(pTempBuffer);
    6247           7 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    6248             : 
    6249           7 :     size_t dimIdx = 0;
    6250           7 :     const size_t nDimsMinus1 = nDims - 1;
    6251             :     GByte abyDstNoData[16];
    6252           7 :     CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
    6253           7 :     GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
    6254             :                                     bufferDataType);
    6255             : 
    6256          37 : lbl_next_depth:
    6257          37 :     if (dimIdx == nDimsMinus1)
    6258             :     {
    6259          25 :         auto nIters = count[dimIdx];
    6260          25 :         double *padfVal = stack[dimIdx].src_ptr;
    6261          25 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
    6262             :         while (true)
    6263             :         {
    6264          92 :             if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
    6265             :             {
    6266          88 :                 padfVal[0] = padfVal[0] * dfScale + dfOffset;
    6267          88 :                 if (bDTIsComplex)
    6268             :                 {
    6269           1 :                     padfVal[1] = padfVal[1] * dfScale + dfOffset;
    6270             :                 }
    6271          88 :                 if (bTempBufferNeeded)
    6272             :                 {
    6273          29 :                     GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
    6274             :                                                     dst_ptr, bufferDataType);
    6275             :                 }
    6276             :             }
    6277             :             else
    6278             :             {
    6279           4 :                 memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
    6280             :             }
    6281             : 
    6282          92 :             if ((--nIters) == 0)
    6283          25 :                 break;
    6284          67 :             padfVal += stack[dimIdx].src_inc_offset;
    6285          67 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    6286             :         }
    6287             :     }
    6288             :     else
    6289             :     {
    6290          12 :         stack[dimIdx].nIters = count[dimIdx];
    6291             :         while (true)
    6292             :         {
    6293          30 :             dimIdx++;
    6294          30 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    6295          30 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6296          30 :             goto lbl_next_depth;
    6297          30 :         lbl_return_to_caller:
    6298          30 :             dimIdx--;
    6299          30 :             if ((--stack[dimIdx].nIters) == 0)
    6300          12 :                 break;
    6301          18 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    6302          18 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6303             :         }
    6304             :     }
    6305          37 :     if (dimIdx > 0)
    6306          30 :         goto lbl_return_to_caller;
    6307             : 
    6308           7 :     if (bTempBufferNeeded)
    6309           2 :         VSIFree(pTempBuffer);
    6310           7 :     return true;
    6311             : }
    6312             : 
    6313             : /************************************************************************/
    6314             : /*                             IWrite()                                 */
    6315             : /************************************************************************/
    6316             : 
    6317          16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
    6318             :                                  const size_t *count, const GInt64 *arrayStep,
    6319             :                                  const GPtrDiff_t *bufferStride,
    6320             :                                  const GDALExtendedDataType &bufferDataType,
    6321             :                                  const void *pSrcBuffer)
    6322             : {
    6323          16 :     const double dfScale = m_dfScale;
    6324          16 :     const double dfOffset = m_dfOffset;
    6325          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
    6326             :     const auto dtDouble =
    6327          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
    6328          16 :     const size_t nDTSize = dtDouble.GetSize();
    6329          16 :     const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
    6330             :     const bool bSelfAndParentHaveNoData =
    6331          16 :         m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
    6332          16 :     double dfNoData = 0;
    6333          16 :     if (m_bHasNoData)
    6334             :     {
    6335           7 :         GDALCopyWords64(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
    6336             :                         &dfNoData, GDT_Float64, 0, 1);
    6337             :     }
    6338             : 
    6339          16 :     double adfSrcNoData[2] = {0, 0};
    6340          16 :     if (bSelfAndParentHaveNoData)
    6341             :     {
    6342           7 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
    6343           7 :                                         m_poParent->GetDataType(),
    6344             :                                         &adfSrcNoData[0], dtDouble);
    6345             :     }
    6346             : 
    6347          16 :     const auto nDims = GetDimensions().size();
    6348          16 :     if (nDims == 0)
    6349             :     {
    6350             :         double adfVal[2];
    6351          10 :         GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
    6352             :                                         dtDouble);
    6353          16 :         if (bSelfAndParentHaveNoData &&
    6354           6 :             (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
    6355             :         {
    6356           4 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
    6357           2 :                                      bufferStride, m_poParent->GetDataType(),
    6358           4 :                                      m_poParent->GetRawNoDataValue());
    6359             :         }
    6360             :         else
    6361             :         {
    6362           8 :             adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
    6363           8 :             if (bDTIsComplex)
    6364             :             {
    6365           2 :                 adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
    6366             :             }
    6367           8 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
    6368           8 :                                      bufferStride, dtDouble, &adfVal[0]);
    6369             :         }
    6370             :     }
    6371             : 
    6372          12 :     std::vector<GPtrDiff_t> tmpBufferStrideVector;
    6373           6 :     size_t nElts = 1;
    6374           6 :     tmpBufferStrideVector.resize(nDims);
    6375          20 :     for (size_t i = 0; i < nDims; i++)
    6376          14 :         nElts *= count[i];
    6377           6 :     tmpBufferStrideVector.back() = 1;
    6378          14 :     for (size_t i = nDims - 1; i > 0;)
    6379             :     {
    6380           8 :         --i;
    6381           8 :         tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
    6382             :     }
    6383           6 :     const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
    6384           6 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
    6385           6 :     if (!pTempBuffer)
    6386           0 :         return false;
    6387             : 
    6388             :     struct Stack
    6389             :     {
    6390             :         size_t nIters = 0;
    6391             :         double *dst_ptr = nullptr;
    6392             :         const GByte *src_ptr = nullptr;
    6393             :         GPtrDiff_t src_inc_offset = 0;
    6394             :         GPtrDiff_t dst_inc_offset = 0;
    6395             :     };
    6396             : 
    6397           6 :     std::vector<Stack> stack(nDims);
    6398           6 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    6399          20 :     for (size_t i = 0; i < nDims; i++)
    6400             :     {
    6401          28 :         stack[i].dst_inc_offset =
    6402          14 :             tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
    6403          14 :         stack[i].src_inc_offset =
    6404          14 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6405             :     }
    6406           6 :     stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
    6407           6 :     stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
    6408             : 
    6409           6 :     size_t dimIdx = 0;
    6410           6 :     const size_t nDimsMinus1 = nDims - 1;
    6411             : 
    6412          34 : lbl_next_depth:
    6413          34 :     if (dimIdx == nDimsMinus1)
    6414             :     {
    6415          23 :         auto nIters = count[dimIdx];
    6416          23 :         double *dst_ptr = stack[dimIdx].dst_ptr;
    6417          23 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
    6418             :         while (true)
    6419             :         {
    6420             :             double adfVal[2];
    6421             :             const double *padfSrcVal;
    6422          86 :             if (bIsBufferDataTypeNativeDataType)
    6423             :             {
    6424          50 :                 padfSrcVal = reinterpret_cast<const double *>(src_ptr);
    6425             :             }
    6426             :             else
    6427             :             {
    6428          36 :                 GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
    6429             :                                                 &adfVal[0], dtDouble);
    6430          36 :                 padfSrcVal = adfVal;
    6431             :             }
    6432             : 
    6433         148 :             if (bSelfAndParentHaveNoData &&
    6434          62 :                 (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
    6435             :             {
    6436           3 :                 dst_ptr[0] = adfSrcNoData[0];
    6437           3 :                 if (bDTIsComplex)
    6438             :                 {
    6439           1 :                     dst_ptr[1] = adfSrcNoData[1];
    6440             :                 }
    6441             :             }
    6442             :             else
    6443             :             {
    6444          83 :                 dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
    6445          83 :                 if (bDTIsComplex)
    6446             :                 {
    6447           1 :                     dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
    6448             :                 }
    6449             :             }
    6450             : 
    6451          86 :             if ((--nIters) == 0)
    6452          23 :                 break;
    6453          63 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    6454          63 :             src_ptr += stack[dimIdx].src_inc_offset;
    6455          63 :         }
    6456             :     }
    6457             :     else
    6458             :     {
    6459          11 :         stack[dimIdx].nIters = count[dimIdx];
    6460             :         while (true)
    6461             :         {
    6462          28 :             dimIdx++;
    6463          28 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    6464          28 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6465          28 :             goto lbl_next_depth;
    6466          28 :         lbl_return_to_caller:
    6467          28 :             dimIdx--;
    6468          28 :             if ((--stack[dimIdx].nIters) == 0)
    6469          11 :                 break;
    6470          17 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    6471          17 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6472             :         }
    6473             :     }
    6474          34 :     if (dimIdx > 0)
    6475          28 :         goto lbl_return_to_caller;
    6476             : 
    6477             :     // If the parent array is not double/complex-double, then convert the
    6478             :     // values to it, before calling Write(), as some implementations can be
    6479             :     // very slow when doing the type conversion.
    6480           6 :     const auto &eParentDT = m_poParent->GetDataType();
    6481           6 :     const size_t nParentDTSize = eParentDT.GetSize();
    6482           6 :     if (nParentDTSize <= nDTSize / 2)
    6483             :     {
    6484             :         // Copy in-place by making sure that source and target do not overlap
    6485           6 :         const auto eNumericDT = dtDouble.GetNumericDataType();
    6486           6 :         const auto eParentNumericDT = eParentDT.GetNumericDataType();
    6487             : 
    6488             :         // Copy first element
    6489             :         {
    6490           6 :             std::vector<GByte> abyTemp(nParentDTSize);
    6491           6 :             GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
    6492           6 :                             static_cast<int>(nDTSize), &abyTemp[0],
    6493             :                             eParentNumericDT, static_cast<int>(nParentDTSize),
    6494             :                             1);
    6495           6 :             memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
    6496             :         }
    6497             :         // Remaining elements
    6498          86 :         for (size_t i = 1; i < nElts; ++i)
    6499             :         {
    6500          80 :             GDALCopyWords64(
    6501          80 :                 static_cast<GByte *>(pTempBuffer) + i * nDTSize, eNumericDT, 0,
    6502          80 :                 static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
    6503             :                 eParentNumericDT, 0, 1);
    6504             :         }
    6505             :     }
    6506             : 
    6507             :     const bool ret =
    6508           6 :         m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
    6509             :                           eParentDT, pTempBuffer);
    6510             : 
    6511           6 :     VSIFree(pTempBuffer);
    6512           6 :     return ret;
    6513             : }
    6514             : 
    6515             : /************************************************************************/
    6516             : /*                           GetUnscaled()                              */
    6517             : /************************************************************************/
    6518             : 
    6519             : /** Return an array that is the unscaled version of the current one.
    6520             :  *
    6521             :  * That is each value of the unscaled array will be
    6522             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    6523             :  *
    6524             :  * Starting with GDAL 3.3, the Write() method is implemented and will convert
    6525             :  * from unscaled values to raw values.
    6526             :  *
    6527             :  * This is the same as the C function GDALMDArrayGetUnscaled().
    6528             :  *
    6529             :  * @param dfOverriddenScale Custom scale value instead of GetScale()
    6530             :  * @param dfOverriddenOffset Custom offset value instead of GetOffset()
    6531             :  * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
    6532             :  * @return a new array, that holds a reference to the original one, and thus is
    6533             :  * a view of it (not a copy), or nullptr in case of error.
    6534             :  */
    6535             : std::shared_ptr<GDALMDArray>
    6536          17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
    6537             :                          double dfOverriddenDstNodata) const
    6538             : {
    6539          34 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    6540          17 :     if (!self)
    6541             :     {
    6542           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6543             :                  "Driver implementation issue: m_pSelf not set !");
    6544           0 :         return nullptr;
    6545             :     }
    6546          17 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    6547             :     {
    6548           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6549             :                  "GetUnscaled() only supports numeric data type");
    6550           0 :         return nullptr;
    6551             :     }
    6552             :     const double dfScale =
    6553          17 :         std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
    6554             :     const double dfOffset =
    6555          17 :         std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
    6556          17 :     if (dfScale == 1.0 && dfOffset == 0.0)
    6557           4 :         return self;
    6558             : 
    6559          13 :     GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
    6560          13 :                            ? GDT_CFloat64
    6561          13 :                            : GDT_Float64;
    6562          14 :     if (dfOverriddenScale == -1 && dfOverriddenOffset == 0 &&
    6563           1 :         GetDataType().GetNumericDataType() == GDT_Float32)
    6564           1 :         eDT = GDT_Float32;
    6565             : 
    6566          26 :     return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
    6567          13 :                                        dfOverriddenDstNodata, eDT);
    6568             : }
    6569             : 
    6570             : /************************************************************************/
    6571             : /*                         GDALMDArrayMask                              */
    6572             : /************************************************************************/
    6573             : 
    6574             : class GDALMDArrayMask final : public GDALPamMDArray
    6575             : {
    6576             :   private:
    6577             :     std::shared_ptr<GDALMDArray> m_poParent{};
    6578             :     GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_Byte)};
    6579             :     double m_dfMissingValue = 0.0;
    6580             :     bool m_bHasMissingValue = false;
    6581             :     double m_dfFillValue = 0.0;
    6582             :     bool m_bHasFillValue = false;
    6583             :     double m_dfValidMin = 0.0;
    6584             :     bool m_bHasValidMin = false;
    6585             :     double m_dfValidMax = 0.0;
    6586             :     bool m_bHasValidMax = false;
    6587             :     std::vector<uint32_t> m_anValidFlagMasks{};
    6588             :     std::vector<uint32_t> m_anValidFlagValues{};
    6589             : 
    6590             :     bool Init(CSLConstList papszOptions);
    6591             : 
    6592             :     template <typename Type>
    6593             :     void
    6594             :     ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
    6595             :                  const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    6596             :                  const void *pTempBuffer,
    6597             :                  const GDALExtendedDataType &oTmpBufferDT,
    6598             :                  const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
    6599             : 
    6600             :   protected:
    6601          45 :     explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
    6602          90 :         : GDALAbstractMDArray(std::string(),
    6603          90 :                               "Mask of " + poParent->GetFullName()),
    6604          90 :           GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
    6605          90 :                          GDALPamMultiDim::GetPAM(poParent),
    6606             :                          poParent->GetContext()),
    6607         225 :           m_poParent(std::move(poParent))
    6608             :     {
    6609          45 :     }
    6610             : 
    6611             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6612             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    6613             :                const GDALExtendedDataType &bufferDataType,
    6614             :                void *pDstBuffer) const override;
    6615             : 
    6616           0 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6617             :                      CSLConstList papszOptions) const override
    6618             :     {
    6619           0 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
    6620             :     }
    6621             : 
    6622             :   public:
    6623             :     static std::shared_ptr<GDALMDArrayMask>
    6624             :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    6625             :            CSLConstList papszOptions);
    6626             : 
    6627           1 :     bool IsWritable() const override
    6628             :     {
    6629           1 :         return false;
    6630             :     }
    6631             : 
    6632          48 :     const std::string &GetFilename() const override
    6633             :     {
    6634          48 :         return m_poParent->GetFilename();
    6635             :     }
    6636             : 
    6637             :     const std::vector<std::shared_ptr<GDALDimension>> &
    6638         373 :     GetDimensions() const override
    6639             :     {
    6640         373 :         return m_poParent->GetDimensions();
    6641             :     }
    6642             : 
    6643         132 :     const GDALExtendedDataType &GetDataType() const override
    6644             :     {
    6645         132 :         return m_dt;
    6646             :     }
    6647             : 
    6648           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    6649             :     {
    6650           1 :         return m_poParent->GetSpatialRef();
    6651             :     }
    6652             : 
    6653           2 :     std::vector<GUInt64> GetBlockSize() const override
    6654             :     {
    6655           2 :         return m_poParent->GetBlockSize();
    6656             :     }
    6657             : };
    6658             : 
    6659             : /************************************************************************/
    6660             : /*                    GDALMDArrayMask::Create()                         */
    6661             : /************************************************************************/
    6662             : 
    6663             : /* static */ std::shared_ptr<GDALMDArrayMask>
    6664          45 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
    6665             :                         CSLConstList papszOptions)
    6666             : {
    6667          90 :     auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
    6668          45 :     newAr->SetSelf(newAr);
    6669          45 :     if (!newAr->Init(papszOptions))
    6670           6 :         return nullptr;
    6671          39 :     return newAr;
    6672             : }
    6673             : 
    6674             : /************************************************************************/
    6675             : /*                    GDALMDArrayMask::Init()                           */
    6676             : /************************************************************************/
    6677             : 
    6678          45 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
    6679             : {
    6680             :     const auto GetSingleValNumericAttr =
    6681         180 :         [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
    6682             :     {
    6683         540 :         auto poAttr = m_poParent->GetAttribute(pszAttrName);
    6684         180 :         if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
    6685             :         {
    6686          22 :             const auto anDimSizes = poAttr->GetDimensionsSize();
    6687          21 :             if (anDimSizes.empty() ||
    6688          10 :                 (anDimSizes.size() == 1 && anDimSizes[0] == 1))
    6689             :             {
    6690          11 :                 bHasVal = true;
    6691          11 :                 dfVal = poAttr->ReadAsDouble();
    6692             :             }
    6693             :         }
    6694         180 :     };
    6695             : 
    6696          45 :     GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
    6697          45 :                             m_dfMissingValue);
    6698          45 :     GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
    6699          45 :     GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
    6700          45 :     GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
    6701             : 
    6702             :     {
    6703         135 :         auto poValidRange = m_poParent->GetAttribute("valid_range");
    6704          50 :         if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
    6705          55 :             poValidRange->GetDimensionsSize()[0] == 2 &&
    6706           5 :             poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
    6707             :         {
    6708           5 :             m_bHasValidMin = true;
    6709           5 :             m_bHasValidMax = true;
    6710           5 :             auto vals = poValidRange->ReadAsDoubleArray();
    6711           5 :             CPLAssert(vals.size() == 2);
    6712           5 :             m_dfValidMin = vals[0];
    6713           5 :             m_dfValidMax = vals[1];
    6714             :         }
    6715             :     }
    6716             : 
    6717             :     // Take into account
    6718             :     // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
    6719             :     // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
    6720             :     const char *pszUnmaskFlags =
    6721          45 :         CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
    6722          45 :     if (pszUnmaskFlags)
    6723             :     {
    6724             :         const auto IsScalarStringAttr =
    6725          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
    6726             :         {
    6727          26 :             return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
    6728          26 :                    (poAttr->GetDimensionsSize().empty() ||
    6729          13 :                     (poAttr->GetDimensionsSize().size() == 1 &&
    6730          26 :                      poAttr->GetDimensionsSize()[0] == 1));
    6731             :         };
    6732             : 
    6733          28 :         auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
    6734          14 :         if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
    6735             :         {
    6736           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6737             :                      "UNMASK_FLAGS option specified but array has no "
    6738             :                      "flag_meanings attribute");
    6739           1 :             return false;
    6740             :         }
    6741          13 :         const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
    6742          13 :         if (!pszFlagMeanings)
    6743             :         {
    6744           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6745             :                      "Cannot read flag_meanings attribute");
    6746           1 :             return false;
    6747             :         }
    6748             : 
    6749             :         const auto IsSingleDimNumericAttr =
    6750          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
    6751             :         {
    6752          26 :             return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
    6753          26 :                    poAttr->GetDimensionsSize().size() == 1;
    6754             :         };
    6755             : 
    6756          24 :         auto poFlagValues = m_poParent->GetAttribute("flag_values");
    6757             :         const bool bHasFlagValues =
    6758          12 :             poFlagValues && IsSingleDimNumericAttr(poFlagValues);
    6759             : 
    6760          24 :         auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
    6761             :         const bool bHasFlagMasks =
    6762          12 :             poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
    6763             : 
    6764          12 :         if (!bHasFlagValues && !bHasFlagMasks)
    6765             :         {
    6766           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6767             :                      "Cannot find flag_values and/or flag_masks attribute");
    6768           1 :             return false;
    6769             :         }
    6770             : 
    6771             :         const CPLStringList aosUnmaskFlags(
    6772          11 :             CSLTokenizeString2(pszUnmaskFlags, ",", 0));
    6773             :         const CPLStringList aosFlagMeanings(
    6774          11 :             CSLTokenizeString2(pszFlagMeanings, " ", 0));
    6775             : 
    6776          11 :         if (bHasFlagValues)
    6777             :         {
    6778           7 :             const auto eType = poFlagValues->GetDataType().GetNumericDataType();
    6779             :             // We could support Int64 or UInt64, but more work...
    6780           7 :             if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
    6781           0 :                 eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
    6782             :             {
    6783           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    6784             :                          "Unsupported data type for flag_values attribute: %s",
    6785             :                          GDALGetDataTypeName(eType));
    6786           0 :                 return false;
    6787             :             }
    6788             :         }
    6789             : 
    6790          11 :         if (bHasFlagMasks)
    6791             :         {
    6792           6 :             const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
    6793             :             // We could support Int64 or UInt64, but more work...
    6794           6 :             if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
    6795           0 :                 eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
    6796             :             {
    6797           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    6798             :                          "Unsupported data type for flag_masks attribute: %s",
    6799             :                          GDALGetDataTypeName(eType));
    6800           0 :                 return false;
    6801             :             }
    6802             :         }
    6803             : 
    6804             :         const std::vector<double> adfValues(
    6805             :             bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
    6806          11 :                            : std::vector<double>());
    6807             :         const std::vector<double> adfMasks(
    6808             :             bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
    6809          11 :                           : std::vector<double>());
    6810             : 
    6811          18 :         if (bHasFlagValues &&
    6812           7 :             adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
    6813             :         {
    6814           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6815             :                      "Number of values in flag_values attribute is different "
    6816             :                      "from the one in flag_meanings");
    6817           1 :             return false;
    6818             :         }
    6819             : 
    6820          16 :         if (bHasFlagMasks &&
    6821           6 :             adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
    6822             :         {
    6823           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6824             :                      "Number of values in flag_masks attribute is different "
    6825             :                      "from the one in flag_meanings");
    6826           1 :             return false;
    6827             :         }
    6828             : 
    6829          19 :         for (int i = 0; i < aosUnmaskFlags.size(); ++i)
    6830             :         {
    6831          11 :             const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
    6832          11 :             if (nIdxFlag < 0)
    6833             :             {
    6834           1 :                 CPLError(
    6835             :                     CE_Failure, CPLE_AppDefined,
    6836             :                     "Cannot fing flag %s in flag_meanings = '%s' attribute",
    6837             :                     aosUnmaskFlags[i], pszFlagMeanings);
    6838           1 :                 return false;
    6839             :             }
    6840             : 
    6841          10 :             if (bHasFlagValues && adfValues[nIdxFlag] < 0)
    6842             :             {
    6843           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    6844             :                          "Invalid value in flag_values[%d] = %f", nIdxFlag,
    6845           0 :                          adfValues[nIdxFlag]);
    6846           0 :                 return false;
    6847             :             }
    6848             : 
    6849          10 :             if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
    6850             :             {
    6851           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    6852             :                          "Invalid value in flag_masks[%d] = %f", nIdxFlag,
    6853           0 :                          adfMasks[nIdxFlag]);
    6854           0 :                 return false;
    6855             :             }
    6856             : 
    6857          10 :             if (bHasFlagValues)
    6858             :             {
    6859          12 :                 m_anValidFlagValues.push_back(
    6860           6 :                     static_cast<uint32_t>(adfValues[nIdxFlag]));
    6861             :             }
    6862             : 
    6863          10 :             if (bHasFlagMasks)
    6864             :             {
    6865          12 :                 m_anValidFlagMasks.push_back(
    6866           6 :                     static_cast<uint32_t>(adfMasks[nIdxFlag]));
    6867             :             }
    6868             :         }
    6869             :     }
    6870             : 
    6871          39 :     return true;
    6872             : }
    6873             : 
    6874             : /************************************************************************/
    6875             : /*                             IRead()                                  */
    6876             : /************************************************************************/
    6877             : 
    6878          48 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6879             :                             const GInt64 *arrayStep,
    6880             :                             const GPtrDiff_t *bufferStride,
    6881             :                             const GDALExtendedDataType &bufferDataType,
    6882             :                             void *pDstBuffer) const
    6883             : {
    6884          48 :     size_t nElts = 1;
    6885          48 :     const size_t nDims = GetDimensionCount();
    6886          96 :     std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
    6887         132 :     for (size_t i = 0; i < nDims; i++)
    6888          84 :         nElts *= count[i];
    6889          48 :     if (nDims > 0)
    6890             :     {
    6891          43 :         tmpBufferStrideVector.back() = 1;
    6892          84 :         for (size_t i = nDims - 1; i > 0;)
    6893             :         {
    6894          41 :             --i;
    6895          41 :             tmpBufferStrideVector[i] =
    6896          41 :                 tmpBufferStrideVector[i + 1] * count[i + 1];
    6897             :         }
    6898             :     }
    6899             : 
    6900             :     /* Optimized case: if we are an integer data type and that there is no */
    6901             :     /* attribute that can be used to set mask = 0, then fill the mask buffer */
    6902             :     /* directly */
    6903          46 :     if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
    6904          70 :         !m_bHasValidMax && m_anValidFlagValues.empty() &&
    6905          32 :         m_anValidFlagMasks.empty() &&
    6906         103 :         m_poParent->GetRawNoDataValue() == nullptr &&
    6907           9 :         GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
    6908             :     {
    6909           7 :         const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
    6910           7 :         if (bBufferDataTypeIsByte)  // Byte case
    6911             :         {
    6912           4 :             bool bContiguous = true;
    6913          10 :             for (size_t i = 0; i < nDims; i++)
    6914             :             {
    6915           7 :                 if (bufferStride[i] != tmpBufferStrideVector[i])
    6916             :                 {
    6917           1 :                     bContiguous = false;
    6918           1 :                     break;
    6919             :                 }
    6920             :             }
    6921           4 :             if (bContiguous)
    6922             :             {
    6923             :                 // CPLDebug("GDAL", "GetMask(): contiguous case");
    6924           3 :                 memset(pDstBuffer, 1, nElts);
    6925           3 :                 return true;
    6926             :             }
    6927             :         }
    6928             : 
    6929             :         struct Stack
    6930             :         {
    6931             :             size_t nIters = 0;
    6932             :             GByte *dst_ptr = nullptr;
    6933             :             GPtrDiff_t dst_inc_offset = 0;
    6934             :         };
    6935             : 
    6936           4 :         std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
    6937           4 :         const size_t nBufferDTSize = bufferDataType.GetSize();
    6938          13 :         for (size_t i = 0; i < nDims; i++)
    6939             :         {
    6940           9 :             stack[i].dst_inc_offset =
    6941           9 :                 static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6942             :         }
    6943           4 :         stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    6944             : 
    6945           4 :         size_t dimIdx = 0;
    6946           4 :         const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
    6947             :         GByte abyOne[16];  // 16 is sizeof GDT_CFloat64
    6948           4 :         CPLAssert(nBufferDTSize <= 16);
    6949           4 :         const GByte flag = 1;
    6950             :         // Coverity misses that m_dt is of type Byte
    6951             :         // coverity[overrun-buffer-val]
    6952           4 :         GDALExtendedDataType::CopyValue(&flag, m_dt, abyOne, bufferDataType);
    6953             : 
    6954          28 :     lbl_next_depth:
    6955          28 :         if (dimIdx == nDimsMinus1)
    6956             :         {
    6957          19 :             auto nIters = nDims > 0 ? count[dimIdx] : 1;
    6958          19 :             GByte *dst_ptr = stack[dimIdx].dst_ptr;
    6959             : 
    6960             :             while (true)
    6961             :             {
    6962             :                 // cppcheck-suppress knownConditionTrueFalse
    6963          73 :                 if (bBufferDataTypeIsByte)
    6964             :                 {
    6965          24 :                     *dst_ptr = flag;
    6966             :                 }
    6967             :                 else
    6968             :                 {
    6969          49 :                     memcpy(dst_ptr, abyOne, nBufferDTSize);
    6970             :                 }
    6971             : 
    6972          73 :                 if ((--nIters) == 0)
    6973          19 :                     break;
    6974          54 :                 dst_ptr += stack[dimIdx].dst_inc_offset;
    6975             :             }
    6976             :         }
    6977             :         else
    6978             :         {
    6979           9 :             stack[dimIdx].nIters = count[dimIdx];
    6980             :             while (true)
    6981             :             {
    6982          24 :                 dimIdx++;
    6983          24 :                 stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6984          24 :                 goto lbl_next_depth;
    6985          24 :             lbl_return_to_caller:
    6986          24 :                 dimIdx--;
    6987          24 :                 if ((--stack[dimIdx].nIters) == 0)
    6988           9 :                     break;
    6989          15 :                 stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6990             :             }
    6991             :         }
    6992          28 :         if (dimIdx > 0)
    6993          24 :             goto lbl_return_to_caller;
    6994             : 
    6995           4 :         return true;
    6996             :     }
    6997             : 
    6998             :     const auto oTmpBufferDT =
    6999          41 :         GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
    7000             :             ? GDALExtendedDataType::Create(GDT_Float64)
    7001          82 :             : m_poParent->GetDataType();
    7002          41 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
    7003          41 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
    7004          41 :     if (!pTempBuffer)
    7005           0 :         return false;
    7006          82 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
    7007          41 :                           tmpBufferStrideVector.data(), oTmpBufferDT,
    7008             :                           pTempBuffer))
    7009             :     {
    7010           0 :         VSIFree(pTempBuffer);
    7011           0 :         return false;
    7012             :     }
    7013             : 
    7014          41 :     switch (oTmpBufferDT.GetNumericDataType())
    7015             :     {
    7016           6 :         case GDT_Byte:
    7017           6 :             ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
    7018             :                                 pTempBuffer, oTmpBufferDT,
    7019             :                                 tmpBufferStrideVector);
    7020           6 :             break;
    7021             : 
    7022           0 :         case GDT_Int8:
    7023           0 :             ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
    7024             :                                 pTempBuffer, oTmpBufferDT,
    7025             :                                 tmpBufferStrideVector);
    7026           0 :             break;
    7027             : 
    7028           1 :         case GDT_UInt16:
    7029           1 :             ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
    7030             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
    7031             :                                   tmpBufferStrideVector);
    7032           1 :             break;
    7033             : 
    7034          14 :         case GDT_Int16:
    7035          14 :             ReadInternal<GInt16>(count, bufferStride, bufferDataType,
    7036             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7037             :                                  tmpBufferStrideVector);
    7038          14 :             break;
    7039             : 
    7040           1 :         case GDT_UInt32:
    7041           1 :             ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
    7042             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
    7043             :                                   tmpBufferStrideVector);
    7044           1 :             break;
    7045             : 
    7046           5 :         case GDT_Int32:
    7047           5 :             ReadInternal<GInt32>(count, bufferStride, bufferDataType,
    7048             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7049             :                                  tmpBufferStrideVector);
    7050           5 :             break;
    7051             : 
    7052           0 :         case GDT_UInt64:
    7053           0 :             ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
    7054             :                                         pDstBuffer, pTempBuffer, oTmpBufferDT,
    7055             :                                         tmpBufferStrideVector);
    7056           0 :             break;
    7057             : 
    7058           0 :         case GDT_Int64:
    7059           0 :             ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
    7060             :                                        pDstBuffer, pTempBuffer, oTmpBufferDT,
    7061             :                                        tmpBufferStrideVector);
    7062           0 :             break;
    7063             : 
    7064           7 :         case GDT_Float32:
    7065           7 :             ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
    7066             :                                 pTempBuffer, oTmpBufferDT,
    7067             :                                 tmpBufferStrideVector);
    7068           7 :             break;
    7069             : 
    7070           7 :         case GDT_Float64:
    7071           7 :             ReadInternal<double>(count, bufferStride, bufferDataType,
    7072             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    7073             :                                  tmpBufferStrideVector);
    7074           7 :             break;
    7075           0 :         case GDT_Unknown:
    7076             :         case GDT_CInt16:
    7077             :         case GDT_CInt32:
    7078             :         case GDT_CFloat32:
    7079             :         case GDT_CFloat64:
    7080             :         case GDT_TypeCount:
    7081           0 :             CPLAssert(false);
    7082             :             break;
    7083             :     }
    7084             : 
    7085          41 :     VSIFree(pTempBuffer);
    7086             : 
    7087          41 :     return true;
    7088             : }
    7089             : 
    7090             : /************************************************************************/
    7091             : /*                          IsValidForDT()                              */
    7092             : /************************************************************************/
    7093             : 
    7094          38 : template <typename Type> static bool IsValidForDT(double dfVal)
    7095             : {
    7096          38 :     if (std::isnan(dfVal))
    7097           0 :         return false;
    7098          38 :     if (dfVal < static_cast<double>(std::numeric_limits<Type>::lowest()))
    7099           0 :         return false;
    7100          38 :     if (dfVal > static_cast<double>(std::numeric_limits<Type>::max()))
    7101           0 :         return false;
    7102          38 :     return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
    7103             : }
    7104             : 
    7105           9 : template <> bool IsValidForDT<double>(double)
    7106             : {
    7107           9 :     return true;
    7108             : }
    7109             : 
    7110             : /************************************************************************/
    7111             : /*                              IsNan()                                 */
    7112             : /************************************************************************/
    7113             : 
    7114        1038 : template <typename Type> inline bool IsNan(Type)
    7115             : {
    7116        1038 :     return false;
    7117             : }
    7118             : 
    7119          25 : template <> bool IsNan<double>(double val)
    7120             : {
    7121          25 :     return std::isnan(val);
    7122             : }
    7123             : 
    7124          26 : template <> bool IsNan<float>(float val)
    7125             : {
    7126          26 :     return std::isnan(val);
    7127             : }
    7128             : 
    7129             : /************************************************************************/
    7130             : /*                         ReadInternal()                               */
    7131             : /************************************************************************/
    7132             : 
    7133             : template <typename Type>
    7134          41 : void GDALMDArrayMask::ReadInternal(
    7135             :     const size_t *count, const GPtrDiff_t *bufferStride,
    7136             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    7137             :     const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
    7138             :     const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
    7139             : {
    7140          41 :     const size_t nDims = GetDimensionCount();
    7141             : 
    7142         205 :     const auto castValue = [](bool &bHasVal, double dfVal) -> Type
    7143             :     {
    7144         205 :         if (bHasVal)
    7145             :         {
    7146          47 :             if (IsValidForDT<Type>(dfVal))
    7147             :             {
    7148          47 :                 return static_cast<Type>(dfVal);
    7149             :             }
    7150             :             else
    7151             :             {
    7152           0 :                 bHasVal = false;
    7153             :             }
    7154             :         }
    7155         158 :         return 0;
    7156             :     };
    7157             : 
    7158          41 :     const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
    7159          41 :     bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
    7160             :     const Type nNoDataValue =
    7161          41 :         castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
    7162          41 :     bool bHasMissingValue = m_bHasMissingValue;
    7163          41 :     const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
    7164          41 :     bool bHasFillValue = m_bHasFillValue;
    7165          41 :     const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
    7166          41 :     bool bHasValidMin = m_bHasValidMin;
    7167          41 :     const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
    7168          41 :     bool bHasValidMax = m_bHasValidMax;
    7169          41 :     const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
    7170          41 :     const bool bHasValidFlags =
    7171          41 :         !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
    7172             : 
    7173         348 :     const auto IsValidFlag = [this](Type v)
    7174             :     {
    7175          54 :         if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
    7176             :         {
    7177          20 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
    7178             :             {
    7179          12 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
    7180             :                     m_anValidFlagValues[i])
    7181             :                 {
    7182           4 :                     return true;
    7183             :                 }
    7184             :             }
    7185             :         }
    7186          42 :         else if (!m_anValidFlagValues.empty())
    7187             :         {
    7188          49 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
    7189             :             {
    7190          29 :                 if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
    7191             :                 {
    7192           4 :                     return true;
    7193             :                 }
    7194             :             }
    7195             :         }
    7196             :         else /* if( !m_anValidFlagMasks.empty() ) */
    7197             :         {
    7198          31 :             for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
    7199             :             {
    7200          22 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
    7201             :                 {
    7202           9 :                     return true;
    7203             :                 }
    7204             :             }
    7205             :         }
    7206          37 :         return false;
    7207             :     };
    7208             : 
    7209             : #define GET_MASK_FOR_SAMPLE(v)                                                 \
    7210             :     static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
    7211             :                        !(bHasMissingValue && v == nMissingValue) &&            \
    7212             :                        !(bHasFillValue && v == nFillValue) &&                  \
    7213             :                        !(bHasValidMin && v < nValidMin) &&                     \
    7214             :                        !(bHasValidMax && v > nValidMax) &&                     \
    7215             :                        (!bHasValidFlags || IsValidFlag(v)));
    7216             : 
    7217          41 :     const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
    7218             :     /* Optimized case: Byte output and output buffer is contiguous */
    7219          41 :     if (bBufferDataTypeIsByte)
    7220             :     {
    7221          37 :         bool bContiguous = true;
    7222          96 :         for (size_t i = 0; i < nDims; i++)
    7223             :         {
    7224          60 :             if (bufferStride[i] != tmpBufferStrideVector[i])
    7225             :             {
    7226           1 :                 bContiguous = false;
    7227           1 :                 break;
    7228             :             }
    7229             :         }
    7230          37 :         if (bContiguous)
    7231             :         {
    7232          36 :             size_t nElts = 1;
    7233          95 :             for (size_t i = 0; i < nDims; i++)
    7234          59 :                 nElts *= count[i];
    7235             : 
    7236         670 :             for (size_t i = 0; i < nElts; i++)
    7237             :             {
    7238         634 :                 const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
    7239         634 :                 static_cast<GByte *>(pDstBuffer)[i] =
    7240         634 :                     GET_MASK_FOR_SAMPLE(*pSrc);
    7241             :             }
    7242          36 :             return;
    7243             :         }
    7244             :     }
    7245             : 
    7246           5 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
    7247             : 
    7248             :     struct Stack
    7249             :     {
    7250             :         size_t nIters = 0;
    7251             :         const GByte *src_ptr = nullptr;
    7252             :         GByte *dst_ptr = nullptr;
    7253             :         GPtrDiff_t src_inc_offset = 0;
    7254             :         GPtrDiff_t dst_inc_offset = 0;
    7255             :     };
    7256             : 
    7257          10 :     std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
    7258           5 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    7259          15 :     for (size_t i = 0; i < nDims; i++)
    7260             :     {
    7261          20 :         stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
    7262          10 :             tmpBufferStrideVector[i] * nTmpBufferDTSize);
    7263          10 :         stack[i].dst_inc_offset =
    7264          10 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    7265             :     }
    7266           5 :     stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
    7267           5 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    7268             : 
    7269           5 :     size_t dimIdx = 0;
    7270           5 :     const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
    7271             :     GByte abyZeroOrOne[2][16];  // 16 is sizeof GDT_CFloat64
    7272           5 :     CPLAssert(nBufferDTSize <= 16);
    7273          15 :     for (GByte flag = 0; flag <= 1; flag++)
    7274             :     {
    7275             :         // Coverity misses that m_dt is of type Byte
    7276             :         // coverity[overrun-buffer-val]
    7277          10 :         GDALExtendedDataType::CopyValue(&flag, m_dt, abyZeroOrOne[flag],
    7278             :                                         bufferDataType);
    7279             :     }
    7280             : 
    7281          43 : lbl_next_depth:
    7282          43 :     if (dimIdx == nDimsMinus1)
    7283             :     {
    7284          35 :         auto nIters = nDims > 0 ? count[dimIdx] : 1;
    7285          35 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
    7286          35 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
    7287             : 
    7288         420 :         while (true)
    7289             :         {
    7290         455 :             const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
    7291         455 :             const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
    7292             : 
    7293         455 :             if (bBufferDataTypeIsByte)
    7294             :             {
    7295          24 :                 *dst_ptr = flag;
    7296             :             }
    7297             :             else
    7298             :             {
    7299         431 :                 memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
    7300             :             }
    7301             : 
    7302         455 :             if ((--nIters) == 0)
    7303          35 :                 break;
    7304         420 :             src_ptr += stack[dimIdx].src_inc_offset;
    7305         420 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    7306             :         }
    7307             :     }
    7308             :     else
    7309             :     {
    7310           8 :         stack[dimIdx].nIters = count[dimIdx];
    7311             :         while (true)
    7312             :         {
    7313          38 :             dimIdx++;
    7314          38 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    7315          38 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    7316          38 :             goto lbl_next_depth;
    7317          38 :         lbl_return_to_caller:
    7318          38 :             dimIdx--;
    7319          38 :             if ((--stack[dimIdx].nIters) == 0)
    7320           8 :                 break;
    7321          30 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    7322          30 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    7323             :         }
    7324             :     }
    7325          43 :     if (dimIdx > 0)
    7326          38 :         goto lbl_return_to_caller;
    7327             : }
    7328             : 
    7329             : /************************************************************************/
    7330             : /*                            GetMask()                                 */
    7331             : /************************************************************************/
    7332             : 
    7333             : /** Return an array that is a mask for the current array
    7334             : 
    7335             :  This array will be of type Byte, with values set to 0 to indicate invalid
    7336             :  pixels of the current array, and values set to 1 to indicate valid pixels.
    7337             : 
    7338             :  The generic implementation honours the NoDataValue, as well as various
    7339             :  netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
    7340             :  and valid_range.
    7341             : 
    7342             :  Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
    7343             :  can be used to specify strings of the "flag_meanings" attribute
    7344             :  (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
    7345             :  for which pixels matching any of those flags will be set at 1 in the mask array,
    7346             :  and pixels matching none of those flags will be set at 0.
    7347             :  For example, let's consider the following netCDF variable defined with:
    7348             :  \verbatim
    7349             :  l2p_flags:valid_min = 0s ;
    7350             :  l2p_flags:valid_max = 256s ;
    7351             :  l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
    7352             :  l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
    7353             :  \endverbatim
    7354             : 
    7355             :  GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
    7356             :  - for pixel values *outside* valid_range [0,256], the mask value will be 0.
    7357             :  - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
    7358             :    will be 1.
    7359             :  - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
    7360             :    will be 0.
    7361             : 
    7362             :  This is the same as the C function GDALMDArrayGetMask().
    7363             : 
    7364             :  @param papszOptions NULL-terminated list of options, or NULL.
    7365             : 
    7366             :  @return a new array, that holds a reference to the original one, and thus is
    7367             :  a view of it (not a copy), or nullptr in case of error.
    7368             : */
    7369             : std::shared_ptr<GDALMDArray>
    7370          46 : GDALMDArray::GetMask(CSLConstList papszOptions) const
    7371             : {
    7372          92 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    7373          46 :     if (!self)
    7374             :     {
    7375           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    7376             :                  "Driver implementation issue: m_pSelf not set !");
    7377           0 :         return nullptr;
    7378             :     }
    7379          46 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    7380             :     {
    7381           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    7382             :                  "GetMask() only supports numeric data type");
    7383           1 :         return nullptr;
    7384             :     }
    7385          45 :     return GDALMDArrayMask::Create(self, papszOptions);
    7386             : }
    7387             : 
    7388             : /************************************************************************/
    7389             : /*                         IsRegularlySpaced()                          */
    7390             : /************************************************************************/
    7391             : 
    7392             : /** Returns whether an array is a 1D regularly spaced array.
    7393             :  *
    7394             :  * @param[out] dfStart     First value in the array
    7395             :  * @param[out] dfIncrement Increment/spacing between consecutive values.
    7396             :  * @return true if the array is regularly spaced.
    7397             :  */
    7398         181 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
    7399             : {
    7400         181 :     dfStart = 0;
    7401         181 :     dfIncrement = 0;
    7402         181 :     if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
    7403           0 :         return false;
    7404         181 :     const auto nSize = GetDimensions()[0]->GetSize();
    7405         181 :     if (nSize <= 1 || nSize > 10 * 1000 * 1000)
    7406           2 :         return false;
    7407             : 
    7408         179 :     size_t nCount = static_cast<size_t>(nSize);
    7409         358 :     std::vector<double> adfTmp;
    7410             :     try
    7411             :     {
    7412         179 :         adfTmp.resize(nCount);
    7413             :     }
    7414           0 :     catch (const std::exception &)
    7415             :     {
    7416           0 :         return false;
    7417             :     }
    7418             : 
    7419         179 :     GUInt64 anStart[1] = {0};
    7420         179 :     size_t anCount[1] = {nCount};
    7421             : 
    7422             :     const auto IsRegularlySpacedInternal =
    7423       82516 :         [&dfStart, &dfIncrement, &anCount, &adfTmp]()
    7424             :     {
    7425         251 :         dfStart = adfTmp[0];
    7426         251 :         dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
    7427         251 :         if (dfIncrement == 0)
    7428             :         {
    7429           3 :             return false;
    7430             :         }
    7431       20564 :         for (size_t i = 1; i < anCount[0]; i++)
    7432             :         {
    7433       20316 :             if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
    7434       20316 :                 1e-3 * fabs(dfIncrement))
    7435             :             {
    7436           0 :                 return false;
    7437             :             }
    7438             :         }
    7439         248 :         return true;
    7440         179 :     };
    7441             : 
    7442             :     // First try with the first block(s). This can avoid excessive processing
    7443             :     // time, for example with Zarr datasets.
    7444             :     // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
    7445             :     // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
    7446         179 :     const auto nBlockSize = GetBlockSize()[0];
    7447         179 :     if (nCount >= 5 && nBlockSize <= nCount / 2)
    7448             :     {
    7449             :         size_t nReducedCount =
    7450          75 :             std::max<size_t>(3, static_cast<size_t>(nBlockSize));
    7451         436 :         while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
    7452         361 :             nReducedCount *= 2;
    7453          75 :         anCount[0] = nReducedCount;
    7454          75 :         if (!Read(anStart, anCount, nullptr, nullptr,
    7455         150 :                   GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
    7456             :         {
    7457           0 :             return false;
    7458             :         }
    7459          75 :         if (!IsRegularlySpacedInternal())
    7460             :         {
    7461           3 :             return false;
    7462             :         }
    7463             : 
    7464             :         // Get next values
    7465          72 :         anStart[0] = nReducedCount;
    7466          72 :         anCount[0] = nCount - nReducedCount;
    7467             :     }
    7468             : 
    7469         176 :     if (!Read(anStart, anCount, nullptr, nullptr,
    7470         352 :               GDALExtendedDataType::Create(GDT_Float64),
    7471         176 :               &adfTmp[static_cast<size_t>(anStart[0])]))
    7472             :     {
    7473           0 :         return false;
    7474             :     }
    7475             : 
    7476         176 :     return IsRegularlySpacedInternal();
    7477             : }
    7478             : 
    7479             : /************************************************************************/
    7480             : /*                         GuessGeoTransform()                          */
    7481             : /************************************************************************/
    7482             : 
    7483             : /** Returns whether 2 specified dimensions form a geotransform
    7484             :  *
    7485             :  * @param nDimX                Index of the X axis.
    7486             :  * @param nDimY                Index of the Y axis.
    7487             :  * @param bPixelIsPoint        Whether the geotransform should be returned
    7488             :  *                             with the pixel-is-point (pixel-center) convention
    7489             :  *                             (bPixelIsPoint = true), or with the pixel-is-area
    7490             :  *                             (top left corner convention)
    7491             :  *                             (bPixelIsPoint = false)
    7492             :  * @param[out] adfGeoTransform Computed geotransform
    7493             :  * @return true if a geotransform could be computed.
    7494             :  */
    7495         214 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
    7496             :                                     bool bPixelIsPoint,
    7497             :                                     double adfGeoTransform[6]) const
    7498             : {
    7499         214 :     const auto &dims(GetDimensions());
    7500         428 :     auto poVarX = dims[nDimX]->GetIndexingVariable();
    7501         428 :     auto poVarY = dims[nDimY]->GetIndexingVariable();
    7502         214 :     double dfXStart = 0.0;
    7503         214 :     double dfXSpacing = 0.0;
    7504         214 :     double dfYStart = 0.0;
    7505         214 :     double dfYSpacing = 0.0;
    7506         488 :     if (poVarX && poVarX->GetDimensionCount() == 1 &&
    7507         274 :         poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
    7508         319 :         poVarY && poVarY->GetDimensionCount() == 1 &&
    7509          91 :         poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
    7510         437 :         poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
    7511          86 :         poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
    7512             :     {
    7513          86 :         adfGeoTransform[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
    7514          86 :         adfGeoTransform[1] = dfXSpacing;
    7515          86 :         adfGeoTransform[2] = 0;
    7516          86 :         adfGeoTransform[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
    7517          86 :         adfGeoTransform[4] = 0;
    7518          86 :         adfGeoTransform[5] = dfYSpacing;
    7519          86 :         return true;
    7520             :     }
    7521         128 :     return false;
    7522             : }
    7523             : 
    7524             : /************************************************************************/
    7525             : /*                       GDALMDArrayResampled                           */
    7526             : /************************************************************************/
    7527             : 
    7528             : class GDALMDArrayResampledDataset;
    7529             : 
    7530             : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
    7531             : {
    7532             :   protected:
    7533             :     CPLErr IReadBlock(int, int, void *) override;
    7534             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
    7535             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
    7536             :                      GDALDataType eBufType, GSpacing nPixelSpaceBuf,
    7537             :                      GSpacing nLineSpaceBuf,
    7538             :                      GDALRasterIOExtraArg *psExtraArg) override;
    7539             : 
    7540             :   public:
    7541             :     explicit GDALMDArrayResampledDatasetRasterBand(
    7542             :         GDALMDArrayResampledDataset *poDSIn);
    7543             : 
    7544             :     double GetNoDataValue(int *pbHasNoData) override;
    7545             : };
    7546             : 
    7547             : class GDALMDArrayResampledDataset final : public GDALPamDataset
    7548             : {
    7549             :     friend class GDALMDArrayResampled;
    7550             :     friend class GDALMDArrayResampledDatasetRasterBand;
    7551             : 
    7552             :     std::shared_ptr<GDALMDArray> m_poArray;
    7553             :     const size_t m_iXDim;
    7554             :     const size_t m_iYDim;
    7555             :     double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
    7556             :     bool m_bHasGT = false;
    7557             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
    7558             : 
    7559             :     std::vector<GUInt64> m_anOffset{};
    7560             :     std::vector<size_t> m_anCount{};
    7561             :     std::vector<GPtrDiff_t> m_anStride{};
    7562             : 
    7563             :     std::string m_osFilenameLong{};
    7564             :     std::string m_osFilenameLat{};
    7565             : 
    7566             :   public:
    7567          24 :     GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
    7568             :                                 size_t iXDim, size_t iYDim)
    7569          24 :         : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
    7570          24 :           m_anOffset(m_poArray->GetDimensionCount(), 0),
    7571          24 :           m_anCount(m_poArray->GetDimensionCount(), 1),
    7572          72 :           m_anStride(m_poArray->GetDimensionCount(), 0)
    7573             :     {
    7574          24 :         const auto &dims(m_poArray->GetDimensions());
    7575             : 
    7576          24 :         nRasterYSize = static_cast<int>(
    7577          24 :             std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
    7578          24 :         nRasterXSize = static_cast<int>(
    7579          24 :             std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
    7580             : 
    7581          24 :         m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false,
    7582          24 :                                                 m_adfGeoTransform);
    7583             : 
    7584          24 :         SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
    7585          24 :     }
    7586             : 
    7587          48 :     ~GDALMDArrayResampledDataset()
    7588          24 :     {
    7589          24 :         if (!m_osFilenameLong.empty())
    7590           5 :             VSIUnlink(m_osFilenameLong.c_str());
    7591          24 :         if (!m_osFilenameLat.empty())
    7592           5 :             VSIUnlink(m_osFilenameLat.c_str());
    7593          48 :     }
    7594             : 
    7595          43 :     CPLErr GetGeoTransform(double *padfGeoTransform) override
    7596             :     {
    7597          43 :         memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
    7598          43 :         return m_bHasGT ? CE_None : CE_Failure;
    7599             :     }
    7600             : 
    7601         105 :     const OGRSpatialReference *GetSpatialRef() const override
    7602             :     {
    7603         105 :         m_poSRS = m_poArray->GetSpatialRef();
    7604         105 :         if (m_poSRS)
    7605             :         {
    7606          79 :             m_poSRS.reset(m_poSRS->Clone());
    7607         158 :             auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
    7608         237 :             for (auto &m : axisMapping)
    7609             :             {
    7610         158 :                 if (m == static_cast<int>(m_iXDim) + 1)
    7611          79 :                     m = 1;
    7612          79 :                 else if (m == static_cast<int>(m_iYDim) + 1)
    7613          79 :                     m = 2;
    7614             :             }
    7615          79 :             m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
    7616             :         }
    7617         105 :         return m_poSRS.get();
    7618             :     }
    7619             : 
    7620           5 :     void SetGeolocationArray(const std::string &osFilenameLong,
    7621             :                              const std::string &osFilenameLat)
    7622             :     {
    7623           5 :         m_osFilenameLong = osFilenameLong;
    7624           5 :         m_osFilenameLat = osFilenameLat;
    7625          10 :         CPLStringList aosGeoLoc;
    7626           5 :         aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
    7627           5 :         aosGeoLoc.SetNameValue("LINE_STEP", "1");
    7628           5 :         aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
    7629           5 :         aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
    7630           5 :         aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG);  // FIXME?
    7631           5 :         aosGeoLoc.SetNameValue("X_BAND", "1");
    7632           5 :         aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
    7633           5 :         aosGeoLoc.SetNameValue("Y_BAND", "1");
    7634           5 :         aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
    7635           5 :         aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
    7636           5 :         SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
    7637           5 :     }
    7638             : };
    7639             : 
    7640             : /************************************************************************/
    7641             : /*                   GDALMDArrayResampledDatasetRasterBand()            */
    7642             : /************************************************************************/
    7643             : 
    7644          24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
    7645          24 :     GDALMDArrayResampledDataset *poDSIn)
    7646             : {
    7647          24 :     const auto &poArray(poDSIn->m_poArray);
    7648          24 :     const auto blockSize(poArray->GetBlockSize());
    7649          24 :     nBlockYSize = (blockSize[poDSIn->m_iYDim])
    7650          24 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    7651          13 :                                                   blockSize[poDSIn->m_iYDim]))
    7652          24 :                       : 1;
    7653          24 :     nBlockXSize = blockSize[poDSIn->m_iXDim]
    7654          13 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    7655          13 :                                                   blockSize[poDSIn->m_iXDim]))
    7656          24 :                       : poDSIn->GetRasterXSize();
    7657          24 :     eDataType = poArray->GetDataType().GetNumericDataType();
    7658          24 :     eAccess = poDSIn->eAccess;
    7659          24 : }
    7660             : 
    7661             : /************************************************************************/
    7662             : /*                           GetNoDataValue()                           */
    7663             : /************************************************************************/
    7664             : 
    7665          50 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
    7666             : {
    7667          50 :     auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
    7668          50 :     const auto &poArray(l_poDS->m_poArray);
    7669          50 :     bool bHasNodata = false;
    7670          50 :     double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
    7671          50 :     if (pbHasNoData)
    7672          46 :         *pbHasNoData = bHasNodata;
    7673          50 :     return dfRes;
    7674             : }
    7675             : 
    7676             : /************************************************************************/
    7677             : /*                            IReadBlock()                              */
    7678             : /************************************************************************/
    7679             : 
    7680           0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
    7681             :                                                          int nBlockYOff,
    7682             :                                                          void *pImage)
    7683             : {
    7684           0 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    7685           0 :     const int nXOff = nBlockXOff * nBlockXSize;
    7686           0 :     const int nYOff = nBlockYOff * nBlockYSize;
    7687           0 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    7688           0 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    7689             :     GDALRasterIOExtraArg sExtraArg;
    7690           0 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    7691           0 :     return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    7692             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    7693           0 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    7694             : }
    7695             : 
    7696             : /************************************************************************/
    7697             : /*                            IRasterIO()                               */
    7698             : /************************************************************************/
    7699             : 
    7700          32 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
    7701             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
    7702             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
    7703             :     GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
    7704             :     GDALRasterIOExtraArg *psExtraArg)
    7705             : {
    7706          32 :     auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
    7707          32 :     const auto &poArray(l_poDS->m_poArray);
    7708          32 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
    7709          32 :     if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
    7710          32 :         nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
    7711          32 :         (nLineSpaceBuf % nBufferDTSize) == 0)
    7712             :     {
    7713          32 :         l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
    7714          32 :         l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
    7715          64 :         l_poDS->m_anStride[l_poDS->m_iXDim] =
    7716          32 :             static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
    7717             : 
    7718          32 :         l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
    7719          32 :         l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
    7720          64 :         l_poDS->m_anStride[l_poDS->m_iYDim] =
    7721          32 :             static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
    7722             : 
    7723          64 :         return poArray->Read(l_poDS->m_anOffset.data(),
    7724          32 :                              l_poDS->m_anCount.data(), nullptr,
    7725          32 :                              l_poDS->m_anStride.data(),
    7726          64 :                              GDALExtendedDataType::Create(eBufType), pData)
    7727          32 :                    ? CE_None
    7728          32 :                    : CE_Failure;
    7729             :     }
    7730           0 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    7731             :                                      pData, nBufXSize, nBufYSize, eBufType,
    7732           0 :                                      nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
    7733             : }
    7734             : 
    7735             : class GDALMDArrayResampled final : public GDALPamMDArray
    7736             : {
    7737             :   private:
    7738             :     std::shared_ptr<GDALMDArray> m_poParent{};
    7739             :     std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
    7740             :     std::vector<GUInt64> m_anBlockSize;
    7741             :     GDALExtendedDataType m_dt;
    7742             :     std::shared_ptr<OGRSpatialReference> m_poSRS{};
    7743             :     std::shared_ptr<GDALMDArray> m_poVarX{};
    7744             :     std::shared_ptr<GDALMDArray> m_poVarY{};
    7745             :     std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
    7746             :     std::unique_ptr<GDALDataset> m_poReprojectedDS{};
    7747             : 
    7748             :   protected:
    7749          21 :     GDALMDArrayResampled(
    7750             :         const std::shared_ptr<GDALMDArray> &poParent,
    7751             :         const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
    7752             :         const std::vector<GUInt64> &anBlockSize)
    7753          42 :         : GDALAbstractMDArray(std::string(),
    7754          42 :                               "Resampled view of " + poParent->GetFullName()),
    7755             :           GDALPamMDArray(
    7756          42 :               std::string(), "Resampled view of " + poParent->GetFullName(),
    7757          42 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
    7758          21 :           m_poParent(std::move(poParent)), m_apoDims(apoDims),
    7759         105 :           m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
    7760             :     {
    7761          21 :         CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
    7762          21 :         CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
    7763          21 :     }
    7764             : 
    7765             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    7766             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    7767             :                const GDALExtendedDataType &bufferDataType,
    7768             :                void *pDstBuffer) const override;
    7769             : 
    7770             :   public:
    7771             :     static std::shared_ptr<GDALMDArray>
    7772             :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    7773             :            const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
    7774             :            GDALRIOResampleAlg resampleAlg,
    7775             :            const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
    7776             : 
    7777          42 :     ~GDALMDArrayResampled()
    7778          21 :     {
    7779             :         // First close the warped VRT
    7780          21 :         m_poReprojectedDS.reset();
    7781          21 :         m_poParentDS.reset();
    7782          42 :     }
    7783             : 
    7784          11 :     bool IsWritable() const override
    7785             :     {
    7786          11 :         return false;
    7787             :     }
    7788             : 
    7789          74 :     const std::string &GetFilename() const override
    7790             :     {
    7791          74 :         return m_poParent->GetFilename();
    7792             :     }
    7793             : 
    7794             :     const std::vector<std::shared_ptr<GDALDimension>> &
    7795         257 :     GetDimensions() const override
    7796             :     {
    7797         257 :         return m_apoDims;
    7798             :     }
    7799             : 
    7800         109 :     const GDALExtendedDataType &GetDataType() const override
    7801             :     {
    7802         109 :         return m_dt;
    7803             :     }
    7804             : 
    7805          21 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    7806             :     {
    7807          21 :         return m_poSRS;
    7808             :     }
    7809             : 
    7810          12 :     std::vector<GUInt64> GetBlockSize() const override
    7811             :     {
    7812          12 :         return m_anBlockSize;
    7813             :     }
    7814             : 
    7815             :     std::shared_ptr<GDALAttribute>
    7816           1 :     GetAttribute(const std::string &osName) const override
    7817             :     {
    7818           1 :         return m_poParent->GetAttribute(osName);
    7819             :     }
    7820             : 
    7821             :     std::vector<std::shared_ptr<GDALAttribute>>
    7822          12 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    7823             :     {
    7824          12 :         return m_poParent->GetAttributes(papszOptions);
    7825             :     }
    7826             : 
    7827           1 :     const std::string &GetUnit() const override
    7828             :     {
    7829           1 :         return m_poParent->GetUnit();
    7830             :     }
    7831             : 
    7832           1 :     const void *GetRawNoDataValue() const override
    7833             :     {
    7834           1 :         return m_poParent->GetRawNoDataValue();
    7835             :     }
    7836             : 
    7837           1 :     double GetOffset(bool *pbHasOffset,
    7838             :                      GDALDataType *peStorageType) const override
    7839             :     {
    7840           1 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    7841             :     }
    7842             : 
    7843           1 :     double GetScale(bool *pbHasScale,
    7844             :                     GDALDataType *peStorageType) const override
    7845             :     {
    7846           1 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    7847             :     }
    7848             : };
    7849             : 
    7850             : /************************************************************************/
    7851             : /*                   GDALMDArrayResampled::Create()                     */
    7852             : /************************************************************************/
    7853             : 
    7854          29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
    7855             :     const std::shared_ptr<GDALMDArray> &poParent,
    7856             :     const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
    7857             :     GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
    7858             :     CSLConstList /* papszOptions */)
    7859             : {
    7860          29 :     const char *pszResampleAlg = "nearest";
    7861          29 :     bool unsupported = false;
    7862          29 :     switch (resampleAlg)
    7863             :     {
    7864          16 :         case GRIORA_NearestNeighbour:
    7865          16 :             pszResampleAlg = "nearest";
    7866          16 :             break;
    7867           2 :         case GRIORA_Bilinear:
    7868           2 :             pszResampleAlg = "bilinear";
    7869           2 :             break;
    7870           5 :         case GRIORA_Cubic:
    7871           5 :             pszResampleAlg = "cubic";
    7872           5 :             break;
    7873           1 :         case GRIORA_CubicSpline:
    7874           1 :             pszResampleAlg = "cubicspline";
    7875           1 :             break;
    7876           1 :         case GRIORA_Lanczos:
    7877           1 :             pszResampleAlg = "lanczos";
    7878           1 :             break;
    7879           1 :         case GRIORA_Average:
    7880           1 :             pszResampleAlg = "average";
    7881           1 :             break;
    7882           1 :         case GRIORA_Mode:
    7883           1 :             pszResampleAlg = "mode";
    7884           1 :             break;
    7885           1 :         case GRIORA_Gauss:
    7886           1 :             unsupported = true;
    7887           1 :             break;
    7888           0 :         case GRIORA_RESERVED_START:
    7889           0 :             unsupported = true;
    7890           0 :             break;
    7891           0 :         case GRIORA_RESERVED_END:
    7892           0 :             unsupported = true;
    7893           0 :             break;
    7894           1 :         case GRIORA_RMS:
    7895           1 :             pszResampleAlg = "rms";
    7896           1 :             break;
    7897             :     }
    7898          29 :     if (unsupported)
    7899             :     {
    7900           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    7901             :                  "Unsupported resample method for GetResampled()");
    7902           1 :         return nullptr;
    7903             :     }
    7904             : 
    7905          28 :     if (poParent->GetDimensionCount() < 2)
    7906             :     {
    7907           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    7908             :                  "GetResampled() only supports 2 dimensions or more");
    7909           1 :         return nullptr;
    7910             :     }
    7911             : 
    7912          27 :     const auto &aoParentDims = poParent->GetDimensions();
    7913          27 :     if (apoNewDimsIn.size() != aoParentDims.size())
    7914             :     {
    7915           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    7916             :                  "GetResampled(): apoNewDims size should be the same as "
    7917             :                  "GetDimensionCount()");
    7918           2 :         return nullptr;
    7919             :     }
    7920             : 
    7921          50 :     std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
    7922          25 :     apoNewDims.reserve(apoNewDimsIn.size());
    7923             : 
    7924          50 :     std::vector<GUInt64> anBlockSize;
    7925          25 :     anBlockSize.reserve(apoNewDimsIn.size());
    7926          50 :     const auto &anParentBlockSize = poParent->GetBlockSize();
    7927             : 
    7928          50 :     auto apoParentDims = poParent->GetDimensions();
    7929             :     // Special case for NASA EMIT datasets
    7930          30 :     const bool bYXBandOrder = (apoParentDims.size() == 3 &&
    7931           7 :                                apoParentDims[0]->GetName() == "downtrack" &&
    7932          32 :                                apoParentDims[1]->GetName() == "crosstrack" &&
    7933           2 :                                apoParentDims[2]->GetName() == "bands");
    7934             : 
    7935             :     const size_t iYDimParent =
    7936          25 :         bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
    7937             :     const size_t iXDimParent =
    7938          25 :         bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
    7939             : 
    7940          77 :     for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
    7941             :     {
    7942          53 :         if (i == iYDimParent || i == iXDimParent)
    7943          48 :             continue;
    7944           5 :         if (apoNewDimsIn[i] == nullptr)
    7945             :         {
    7946           3 :             apoNewDims.emplace_back(aoParentDims[i]);
    7947             :         }
    7948           3 :         else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
    7949           1 :                  apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
    7950             :         {
    7951           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    7952             :                      "GetResampled(): apoNewDims[%u] should be the same "
    7953             :                      "as its parent",
    7954             :                      i);
    7955           1 :             return nullptr;
    7956             :         }
    7957             :         else
    7958             :         {
    7959           1 :             apoNewDims.emplace_back(aoParentDims[i]);
    7960             :         }
    7961           4 :         anBlockSize.emplace_back(anParentBlockSize[i]);
    7962             :     }
    7963             : 
    7964             :     std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
    7965          48 :         new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
    7966             : 
    7967          24 :     double dfXStart = 0.0;
    7968          24 :     double dfXSpacing = 0.0;
    7969          24 :     bool gotXSpacing = false;
    7970          48 :     auto poNewDimX = apoNewDimsIn[iXDimParent];
    7971          24 :     if (poNewDimX)
    7972             :     {
    7973           4 :         if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
    7974             :         {
    7975           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    7976             :                      "Too big size for X dimension");
    7977           0 :             return nullptr;
    7978             :         }
    7979           4 :         auto var = poNewDimX->GetIndexingVariable();
    7980           4 :         if (var)
    7981             :         {
    7982           2 :             if (var->GetDimensionCount() != 1 ||
    7983           2 :                 var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
    7984           1 :                 !var->IsRegularlySpaced(dfXStart, dfXSpacing))
    7985             :             {
    7986           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    7987             :                          "New X dimension should be indexed by a regularly "
    7988             :                          "spaced variable");
    7989           0 :                 return nullptr;
    7990             :             }
    7991           1 :             gotXSpacing = true;
    7992             :         }
    7993             :     }
    7994             : 
    7995          24 :     double dfYStart = 0.0;
    7996          24 :     double dfYSpacing = 0.0;
    7997          48 :     auto poNewDimY = apoNewDimsIn[iYDimParent];
    7998          24 :     bool gotYSpacing = false;
    7999          24 :     if (poNewDimY)
    8000             :     {
    8001           4 :         if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
    8002             :         {
    8003           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    8004             :                      "Too big size for Y dimension");
    8005           0 :             return nullptr;
    8006             :         }
    8007           4 :         auto var = poNewDimY->GetIndexingVariable();
    8008           4 :         if (var)
    8009             :         {
    8010           2 :             if (var->GetDimensionCount() != 1 ||
    8011           2 :                 var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
    8012           1 :                 !var->IsRegularlySpaced(dfYStart, dfYSpacing))
    8013             :             {
    8014           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    8015             :                          "New Y dimension should be indexed by a regularly "
    8016             :                          "spaced variable");
    8017           0 :                 return nullptr;
    8018             :             }
    8019           1 :             gotYSpacing = true;
    8020             :         }
    8021             :     }
    8022             : 
    8023             :     // This limitation could probably be removed
    8024          24 :     if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
    8025             :     {
    8026           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    8027             :                  "Either none of new X or Y dimension should have an indexing "
    8028             :                  "variable, or both should both should have one.");
    8029           0 :         return nullptr;
    8030             :     }
    8031             : 
    8032          48 :     std::string osDstWKT;
    8033          24 :     if (poTargetSRS)
    8034             :     {
    8035           2 :         char *pszDstWKT = nullptr;
    8036           2 :         if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
    8037             :         {
    8038           0 :             CPLFree(pszDstWKT);
    8039           0 :             return nullptr;
    8040             :         }
    8041           2 :         osDstWKT = pszDstWKT;
    8042           2 :         CPLFree(pszDstWKT);
    8043             :     }
    8044             : 
    8045             :     // Use coordinate variables for geolocation array
    8046          48 :     const auto apoCoordinateVars = poParent->GetCoordinateVariables();
    8047          24 :     bool useGeolocationArray = false;
    8048          24 :     if (apoCoordinateVars.size() >= 2)
    8049             :     {
    8050           0 :         std::shared_ptr<GDALMDArray> poLongVar;
    8051           0 :         std::shared_ptr<GDALMDArray> poLatVar;
    8052          15 :         for (const auto &poCoordVar : apoCoordinateVars)
    8053             :         {
    8054          10 :             const auto &osName = poCoordVar->GetName();
    8055          30 :             const auto poAttr = poCoordVar->GetAttribute("standard_name");
    8056          20 :             std::string osStandardName;
    8057          12 :             if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
    8058           2 :                 poAttr->GetDimensionCount() == 0)
    8059             :             {
    8060           2 :                 const char *pszStandardName = poAttr->ReadAsString();
    8061           2 :                 if (pszStandardName)
    8062           2 :                     osStandardName = pszStandardName;
    8063             :             }
    8064          21 :             if (osName == "lon" || osName == "longitude" ||
    8065          21 :                 osName == "Longitude" || osStandardName == "longitude")
    8066             :             {
    8067           5 :                 poLongVar = poCoordVar;
    8068             :             }
    8069           6 :             else if (osName == "lat" || osName == "latitude" ||
    8070           6 :                      osName == "Latitude" || osStandardName == "latitude")
    8071             :             {
    8072           5 :                 poLatVar = poCoordVar;
    8073             :             }
    8074             :         }
    8075           5 :         if (poLatVar != nullptr && poLongVar != nullptr)
    8076             :         {
    8077           5 :             const auto longDimCount = poLongVar->GetDimensionCount();
    8078           5 :             const auto &longDims = poLongVar->GetDimensions();
    8079           5 :             const auto latDimCount = poLatVar->GetDimensionCount();
    8080           5 :             const auto &latDims = poLatVar->GetDimensions();
    8081           5 :             const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
    8082           5 :             const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
    8083           0 :             if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
    8084           5 :                 latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
    8085             :             {
    8086             :                 // Geolocation arrays are 1D, and of consistent size with
    8087             :                 // the variable
    8088           0 :                 useGeolocationArray = true;
    8089             :             }
    8090           1 :             else if ((longDimCount == 2 ||
    8091           6 :                       (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
    8092          10 :                      longDims[longDimCount - 2]->GetSize() == yDimSize &&
    8093          10 :                      longDims[longDimCount - 1]->GetSize() == xDimSize &&
    8094           1 :                      (latDimCount == 2 ||
    8095           6 :                       (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
    8096          15 :                      latDims[latDimCount - 2]->GetSize() == yDimSize &&
    8097           5 :                      latDims[latDimCount - 1]->GetSize() == xDimSize)
    8098             : 
    8099             :             {
    8100             :                 // Geolocation arrays are 2D (or 3D with first dimension of
    8101             :                 // size 1, as found in Sentinel 5P products), and of consistent
    8102             :                 // size with the variable
    8103           5 :                 useGeolocationArray = true;
    8104             :             }
    8105             :             else
    8106             :             {
    8107           0 :                 CPLDebug(
    8108             :                     "GDAL",
    8109             :                     "Longitude and latitude coordinate variables found, "
    8110             :                     "but their characteristics are not compatible of using "
    8111             :                     "them as geolocation arrays");
    8112             :             }
    8113           5 :             if (useGeolocationArray)
    8114             :             {
    8115          10 :                 CPLDebug("GDAL",
    8116             :                          "Setting geolocation array from variables %s and %s",
    8117           5 :                          poLongVar->GetName().c_str(),
    8118           5 :                          poLatVar->GetName().c_str());
    8119             :                 const std::string osFilenameLong =
    8120           5 :                     VSIMemGenerateHiddenFilename("longitude.tif");
    8121             :                 const std::string osFilenameLat =
    8122           5 :                     VSIMemGenerateHiddenFilename("latitude.tif");
    8123             :                 std::unique_ptr<GDALDataset> poTmpLongDS(
    8124             :                     longDimCount == 1
    8125           0 :                         ? poLongVar->AsClassicDataset(0, 0)
    8126          20 :                         : poLongVar->AsClassicDataset(longDimCount - 1,
    8127          15 :                                                       longDimCount - 2));
    8128           5 :                 auto hTIFFLongDS = GDALTranslate(
    8129             :                     osFilenameLong.c_str(),
    8130             :                     GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
    8131             :                 std::unique_ptr<GDALDataset> poTmpLatDS(
    8132           0 :                     latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
    8133          20 :                                      : poLatVar->AsClassicDataset(
    8134          15 :                                            latDimCount - 1, latDimCount - 2));
    8135           5 :                 auto hTIFFLatDS = GDALTranslate(
    8136             :                     osFilenameLat.c_str(),
    8137             :                     GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
    8138           5 :                 const bool bError =
    8139           5 :                     (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
    8140           5 :                 GDALClose(hTIFFLongDS);
    8141           5 :                 GDALClose(hTIFFLatDS);
    8142           5 :                 if (bError)
    8143             :                 {
    8144           0 :                     VSIUnlink(osFilenameLong.c_str());
    8145           0 :                     VSIUnlink(osFilenameLat.c_str());
    8146           0 :                     return nullptr;
    8147             :                 }
    8148             : 
    8149           5 :                 poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
    8150             :             }
    8151             :         }
    8152             :         else
    8153             :         {
    8154           0 :             CPLDebug("GDAL",
    8155             :                      "Coordinate variables available for %s, but "
    8156             :                      "longitude and/or latitude variables were not identified",
    8157           0 :                      poParent->GetName().c_str());
    8158             :         }
    8159             :     }
    8160             : 
    8161             :     // Build gdalwarp arguments
    8162          48 :     CPLStringList aosArgv;
    8163             : 
    8164          24 :     aosArgv.AddString("-of");
    8165          24 :     aosArgv.AddString("VRT");
    8166             : 
    8167          24 :     aosArgv.AddString("-r");
    8168          24 :     aosArgv.AddString(pszResampleAlg);
    8169             : 
    8170          24 :     if (!osDstWKT.empty())
    8171             :     {
    8172           2 :         aosArgv.AddString("-t_srs");
    8173           2 :         aosArgv.AddString(osDstWKT.c_str());
    8174             :     }
    8175             : 
    8176          24 :     if (useGeolocationArray)
    8177           5 :         aosArgv.AddString("-geoloc");
    8178             : 
    8179          24 :     if (gotXSpacing && gotYSpacing)
    8180             :     {
    8181           1 :         const double dfXMin = dfXStart - dfXSpacing / 2;
    8182             :         const double dfXMax =
    8183           1 :             dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
    8184           1 :         const double dfYMax = dfYStart - dfYSpacing / 2;
    8185             :         const double dfYMin =
    8186           1 :             dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
    8187           1 :         aosArgv.AddString("-te");
    8188           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfXMin));
    8189           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfYMin));
    8190           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfXMax));
    8191           1 :         aosArgv.AddString(CPLSPrintf("%.17g", dfYMax));
    8192             :     }
    8193             : 
    8194          24 :     if (poNewDimX && poNewDimY)
    8195             :     {
    8196           3 :         aosArgv.AddString("-ts");
    8197             :         aosArgv.AddString(
    8198           3 :             CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
    8199             :         aosArgv.AddString(
    8200           3 :             CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
    8201             :     }
    8202          21 :     else if (poNewDimX)
    8203             :     {
    8204           1 :         aosArgv.AddString("-ts");
    8205             :         aosArgv.AddString(
    8206           1 :             CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
    8207           1 :         aosArgv.AddString("0");
    8208             :     }
    8209          20 :     else if (poNewDimY)
    8210             :     {
    8211           1 :         aosArgv.AddString("-ts");
    8212           1 :         aosArgv.AddString("0");
    8213             :         aosArgv.AddString(
    8214           1 :             CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
    8215             :     }
    8216             : 
    8217             :     // Create a warped VRT dataset
    8218             :     GDALWarpAppOptions *psOptions =
    8219          24 :         GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
    8220          24 :     GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
    8221             :     std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
    8222          48 :         GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
    8223          24 :     GDALWarpAppOptionsFree(psOptions);
    8224          24 :     if (poReprojectedDS == nullptr)
    8225           3 :         return nullptr;
    8226             : 
    8227             :     int nBlockXSize;
    8228             :     int nBlockYSize;
    8229          21 :     poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    8230          21 :     anBlockSize.emplace_back(nBlockYSize);
    8231          21 :     anBlockSize.emplace_back(nBlockXSize);
    8232             : 
    8233          21 :     double adfGeoTransform[6] = {0, 0, 0, 0, 0, 0};
    8234          21 :     CPLErr eErr = poReprojectedDS->GetGeoTransform(adfGeoTransform);
    8235          21 :     CPLAssert(eErr == CE_None);
    8236          21 :     CPL_IGNORE_RET_VAL(eErr);
    8237             : 
    8238             :     auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
    8239           0 :         std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
    8240          42 :         poReprojectedDS->GetRasterYSize());
    8241             :     auto varY = GDALMDArrayRegularlySpaced::Create(
    8242          63 :         std::string(), poDimY->GetName(), poDimY,
    8243          84 :         adfGeoTransform[3] + adfGeoTransform[5] / 2, adfGeoTransform[5], 0);
    8244          21 :     poDimY->SetIndexingVariable(varY);
    8245             : 
    8246             :     auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
    8247           0 :         std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
    8248          42 :         poReprojectedDS->GetRasterXSize());
    8249             :     auto varX = GDALMDArrayRegularlySpaced::Create(
    8250          63 :         std::string(), poDimX->GetName(), poDimX,
    8251          84 :         adfGeoTransform[0] + adfGeoTransform[1] / 2, adfGeoTransform[1], 0);
    8252          21 :     poDimX->SetIndexingVariable(varX);
    8253             : 
    8254          21 :     apoNewDims.emplace_back(poDimY);
    8255          21 :     apoNewDims.emplace_back(poDimX);
    8256             :     auto newAr(std::shared_ptr<GDALMDArrayResampled>(
    8257          42 :         new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
    8258          21 :     newAr->SetSelf(newAr);
    8259          21 :     if (poTargetSRS)
    8260             :     {
    8261           2 :         newAr->m_poSRS.reset(poTargetSRS->Clone());
    8262             :     }
    8263             :     else
    8264             :     {
    8265          19 :         newAr->m_poSRS = poParent->GetSpatialRef();
    8266             :     }
    8267          21 :     newAr->m_poVarX = varX;
    8268          21 :     newAr->m_poVarY = varY;
    8269          21 :     newAr->m_poReprojectedDS = std::move(poReprojectedDS);
    8270          21 :     newAr->m_poParentDS = std::move(poParentDS);
    8271             : 
    8272             :     // If the input array is y,x,band ordered, the above newAr is
    8273             :     // actually band,y,x ordered as it is more convenient for
    8274             :     // GDALMDArrayResampled::IRead() implementation. But transpose that
    8275             :     // array to the order of the input array
    8276          21 :     if (bYXBandOrder)
    8277           4 :         return newAr->Transpose(std::vector<int>{1, 2, 0});
    8278             : 
    8279          19 :     return newAr;
    8280             : }
    8281             : 
    8282             : /************************************************************************/
    8283             : /*                   GDALMDArrayResampled::IRead()                      */
    8284             : /************************************************************************/
    8285             : 
    8286          29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
    8287             :                                  const size_t *count, const GInt64 *arrayStep,
    8288             :                                  const GPtrDiff_t *bufferStride,
    8289             :                                  const GDALExtendedDataType &bufferDataType,
    8290             :                                  void *pDstBuffer) const
    8291             : {
    8292          29 :     if (bufferDataType.GetClass() != GEDTC_NUMERIC)
    8293           0 :         return false;
    8294             : 
    8295             :     struct Stack
    8296             :     {
    8297             :         size_t nIters = 0;
    8298             :         GByte *dst_ptr = nullptr;
    8299             :         GPtrDiff_t dst_inc_offset = 0;
    8300             :     };
    8301             : 
    8302          29 :     const auto nDims = GetDimensionCount();
    8303          58 :     std::vector<Stack> stack(nDims + 1);  // +1 to avoid -Wnull-dereference
    8304          29 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    8305          92 :     for (size_t i = 0; i < nDims; i++)
    8306             :     {
    8307          63 :         stack[i].dst_inc_offset =
    8308          63 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    8309             :     }
    8310          29 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    8311             : 
    8312          29 :     size_t dimIdx = 0;
    8313          29 :     const size_t iDimY = nDims - 2;
    8314          29 :     const size_t iDimX = nDims - 1;
    8315             :     // Use an array to avoid a false positive warning from CLang Static
    8316             :     // Analyzer about flushCaches being never read
    8317          29 :     bool flushCaches[] = {false};
    8318             :     const bool bYXBandOrder =
    8319          29 :         m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
    8320             : 
    8321          38 : lbl_next_depth:
    8322          38 :     if (dimIdx == iDimY)
    8323             :     {
    8324          33 :         if (flushCaches[0])
    8325             :         {
    8326           5 :             flushCaches[0] = false;
    8327             :             // When changing of 2D slice, flush GDAL 2D buffers
    8328           5 :             m_poParentDS->FlushCache(false);
    8329           5 :             m_poReprojectedDS->FlushCache(false);
    8330             :         }
    8331             : 
    8332          33 :         if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
    8333             :                                     GF_Read, iDimX, iDimY, arrayStartIdx, count,
    8334             :                                     arrayStep, bufferStride, bufferDataType,
    8335          33 :                                     stack[dimIdx].dst_ptr))
    8336             :         {
    8337           0 :             return false;
    8338             :         }
    8339             :     }
    8340             :     else
    8341             :     {
    8342           5 :         stack[dimIdx].nIters = count[dimIdx];
    8343           5 :         if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
    8344           5 :             arrayStartIdx[dimIdx])
    8345             :         {
    8346           1 :             flushCaches[0] = true;
    8347             :         }
    8348           5 :         m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
    8349           5 :             arrayStartIdx[dimIdx];
    8350             :         while (true)
    8351             :         {
    8352           9 :             dimIdx++;
    8353           9 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    8354           9 :             goto lbl_next_depth;
    8355           9 :         lbl_return_to_caller:
    8356           9 :             dimIdx--;
    8357           9 :             if ((--stack[dimIdx].nIters) == 0)
    8358           5 :                 break;
    8359           4 :             flushCaches[0] = true;
    8360           4 :             ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
    8361           4 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    8362             :         }
    8363             :     }
    8364          38 :     if (dimIdx > 0)
    8365           9 :         goto lbl_return_to_caller;
    8366             : 
    8367          29 :     return true;
    8368             : }
    8369             : 
    8370             : /************************************************************************/
    8371             : /*                           GetResampled()                             */
    8372             : /************************************************************************/
    8373             : 
    8374             : /** Return an array that is a resampled / reprojected view of the current array
    8375             :  *
    8376             :  * This is the same as the C function GDALMDArrayGetResampled().
    8377             :  *
    8378             :  * Currently this method can only resample along the last 2 dimensions, unless
    8379             :  * orthorectifying a NASA EMIT dataset.
    8380             :  *
    8381             :  * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
    8382             :  * geometry lookup table (GLT) is used by default for fast orthorectification.
    8383             :  *
    8384             :  * Options available are:
    8385             :  * <ul>
    8386             :  * <li>EMIT_ORTHORECTIFICATION=YES/NO: defaults to YES for a NASA EMIT dataset.
    8387             :  * Can be set to NO to use generic reprojection method.
    8388             :  * </li>
    8389             :  * <li>USE_GOOD_WAVELENGTHS=YES/NO: defaults to YES. Only used for EMIT
    8390             :  * orthorectification to take into account the value of the
    8391             :  * /sensor_band_parameters/good_wavelengths array to decide if slices of the
    8392             :  * current array along the band dimension are valid.</li>
    8393             :  * </ul>
    8394             :  *
    8395             :  * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
    8396             :  *                   apoNewDims[i] can be NULL to let the method automatically
    8397             :  *                   determine it.
    8398             :  * @param resampleAlg Resampling algorithm
    8399             :  * @param poTargetSRS Target SRS, or nullptr
    8400             :  * @param papszOptions NULL-terminated list of options, or NULL.
    8401             :  *
    8402             :  * @return a new array, that holds a reference to the original one, and thus is
    8403             :  * a view of it (not a copy), or nullptr in case of error.
    8404             :  *
    8405             :  * @since 3.4
    8406             :  */
    8407          38 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
    8408             :     const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
    8409             :     GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
    8410             :     CSLConstList papszOptions) const
    8411             : {
    8412          76 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    8413          38 :     if (!self)
    8414             :     {
    8415           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8416             :                  "Driver implementation issue: m_pSelf not set !");
    8417           0 :         return nullptr;
    8418             :     }
    8419          38 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    8420             :     {
    8421           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8422             :                  "GetResampled() only supports numeric data type");
    8423           0 :         return nullptr;
    8424             :     }
    8425             : 
    8426             :     // Special case for NASA EMIT datasets
    8427          76 :     auto apoDims = GetDimensions();
    8428          36 :     if (poTargetSRS == nullptr &&
    8429          59 :         ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
    8430          20 :           apoDims[1]->GetName() == "crosstrack" &&
    8431          10 :           apoDims[2]->GetName() == "bands" &&
    8432          48 :           (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
    8433           1 :            apoNewDims ==
    8434          42 :                std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
    8435          30 :                                                            apoDims[2]})) ||
    8436          51 :          (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
    8437           3 :           apoDims[1]->GetName() == "crosstrack" &&
    8438          77 :           apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
    8439          13 :         CPLTestBool(CSLFetchNameValueDef(papszOptions,
    8440             :                                          "EMIT_ORTHORECTIFICATION", "YES")))
    8441             :     {
    8442           9 :         auto poRootGroup = GetRootGroup();
    8443           9 :         if (poRootGroup)
    8444             :         {
    8445          18 :             auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
    8446          18 :             auto poLocationGroup = poRootGroup->OpenGroup("location");
    8447           9 :             if (poAttrGeotransform &&
    8448           9 :                 poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
    8449           9 :                 poAttrGeotransform->GetDimensionCount() == 1 &&
    8450          27 :                 poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
    8451           9 :                 poLocationGroup)
    8452             :             {
    8453          18 :                 auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
    8454          18 :                 auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
    8455          27 :                 if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
    8456          18 :                     poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
    8457          18 :                     poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
    8458          27 :                     poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
    8459          27 :                     poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
    8460           9 :                     poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
    8461             :                 {
    8462             :                     return CreateGLTOrthorectified(
    8463             :                         self, poRootGroup, poGLT_X, poGLT_Y,
    8464             :                         /* nGLTIndexOffset = */ -1,
    8465          18 :                         poAttrGeotransform->ReadAsDoubleArray(), papszOptions);
    8466             :                 }
    8467             :             }
    8468             :         }
    8469             :     }
    8470             : 
    8471          29 :     if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
    8472             :                                          "EMIT_ORTHORECTIFICATION", "NO")))
    8473             :     {
    8474           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8475             :                  "EMIT_ORTHORECTIFICATION required, but dataset and/or "
    8476             :                  "parameters are not compatible with it");
    8477           0 :         return nullptr;
    8478             :     }
    8479             : 
    8480             :     return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
    8481          29 :                                         poTargetSRS, papszOptions);
    8482             : }
    8483             : 
    8484             : /************************************************************************/
    8485             : /*                         GDALDatasetFromArray()                       */
    8486             : /************************************************************************/
    8487             : 
    8488             : class GDALDatasetFromArray;
    8489             : 
    8490             : namespace
    8491             : {
    8492             : struct MetadataItem
    8493             : {
    8494             :     std::shared_ptr<GDALMDArray> poArray{};
    8495             :     std::string osName{};
    8496             :     std::string osDefinition{};
    8497             :     bool bDefinitionUsesPctForG = false;
    8498             : };
    8499             : 
    8500             : struct BandImageryMetadata
    8501             : {
    8502             :     std::shared_ptr<GDALMDArray> poCentralWavelengthArray{};
    8503             :     double dfCentralWavelengthToMicrometer = 1.0;
    8504             :     std::shared_ptr<GDALMDArray> poFWHMArray{};
    8505             :     double dfFWHMToMicrometer = 1.0;
    8506             : };
    8507             : 
    8508             : }  // namespace
    8509             : 
    8510             : class GDALRasterBandFromArray final : public GDALPamRasterBand
    8511             : {
    8512             :     std::vector<GUInt64> m_anOffset{};
    8513             :     std::vector<size_t> m_anCount{};
    8514             :     std::vector<GPtrDiff_t> m_anStride{};
    8515             : 
    8516             :   protected:
    8517             :     CPLErr IReadBlock(int, int, void *) override;
    8518             :     CPLErr IWriteBlock(int, int, void *) override;
    8519             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
    8520             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
    8521             :                      GDALDataType eBufType, GSpacing nPixelSpaceBuf,
    8522             :                      GSpacing nLineSpaceBuf,
    8523             :                      GDALRasterIOExtraArg *psExtraArg) override;
    8524             : 
    8525             :   public:
    8526             :     explicit GDALRasterBandFromArray(
    8527             :         GDALDatasetFromArray *poDSIn,
    8528             :         const std::vector<GUInt64> &anOtherDimCoord,
    8529             :         const std::vector<std::vector<MetadataItem>>
    8530             :             &aoBandParameterMetadataItems,
    8531             :         const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
    8532             :         double dfDelay, time_t nStartTime, bool &bHasWarned);
    8533             : 
    8534             :     double GetNoDataValue(int *pbHasNoData) override;
    8535             :     int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
    8536             :     uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
    8537             :     double GetOffset(int *pbHasOffset) override;
    8538             :     double GetScale(int *pbHasScale) override;
    8539             :     const char *GetUnitType() override;
    8540             :     GDALColorInterp GetColorInterpretation() override;
    8541             : };
    8542             : 
    8543             : class GDALDatasetFromArray final : public GDALPamDataset
    8544             : {
    8545             :     friend class GDALRasterBandFromArray;
    8546             : 
    8547             :     std::shared_ptr<GDALMDArray> m_poArray;
    8548             :     size_t m_iXDim;
    8549             :     size_t m_iYDim;
    8550             :     double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
    8551             :     bool m_bHasGT = false;
    8552             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
    8553             :     GDALMultiDomainMetadata m_oMDD{};
    8554             :     std::string m_osOvrFilename{};
    8555             : 
    8556             :   public:
    8557         190 :     GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
    8558             :                          size_t iXDim, size_t iYDim)
    8559         190 :         : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
    8560             :     {
    8561             :         // Initialize an overview filename from the filename of the array
    8562             :         // and its name.
    8563         190 :         const std::string &osFilename = m_poArray->GetFilename();
    8564         190 :         if (!osFilename.empty())
    8565             :         {
    8566         169 :             m_osOvrFilename = osFilename;
    8567         169 :             m_osOvrFilename += '.';
    8568        6346 :             for (char ch : m_poArray->GetName())
    8569             :             {
    8570        6177 :                 if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
    8571        5478 :                     (ch >= 'a' && ch <= 'z') || ch == '_')
    8572             :                 {
    8573        4940 :                     m_osOvrFilename += ch;
    8574             :                 }
    8575             :                 else
    8576             :                 {
    8577        1237 :                     m_osOvrFilename += '_';
    8578             :                 }
    8579             :             }
    8580         169 :             m_osOvrFilename += ".ovr";
    8581         169 :             oOvManager.Initialize(this);
    8582             :         }
    8583         190 :     }
    8584             : 
    8585             :     static GDALDatasetFromArray *
    8586             :     Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
    8587             :            size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
    8588             :            CSLConstList papszOptions);
    8589             : 
    8590         380 :     ~GDALDatasetFromArray()
    8591         190 :     {
    8592         190 :         GDALDatasetFromArray::Close();
    8593         380 :     }
    8594             : 
    8595         312 :     CPLErr Close() override
    8596             :     {
    8597         312 :         CPLErr eErr = CE_None;
    8598         312 :         if (nOpenFlags != OPEN_FLAGS_CLOSED)
    8599             :         {
    8600         312 :             if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
    8601             :                 CE_None)
    8602           0 :                 eErr = CE_Failure;
    8603         312 :             m_poArray.reset();
    8604             :         }
    8605         312 :         return eErr;
    8606             :     }
    8607             : 
    8608          49 :     CPLErr GetGeoTransform(double *padfGeoTransform) override
    8609             :     {
    8610          49 :         memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
    8611          49 :         return m_bHasGT ? CE_None : CE_Failure;
    8612             :     }
    8613             : 
    8614          57 :     const OGRSpatialReference *GetSpatialRef() const override
    8615             :     {
    8616          57 :         if (m_poArray->GetDimensionCount() < 2)
    8617           3 :             return nullptr;
    8618          54 :         m_poSRS = m_poArray->GetSpatialRef();
    8619          54 :         if (m_poSRS)
    8620             :         {
    8621          20 :             m_poSRS.reset(m_poSRS->Clone());
    8622          40 :             auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
    8623          60 :             for (auto &m : axisMapping)
    8624             :             {
    8625          40 :                 if (m == static_cast<int>(m_iXDim) + 1)
    8626          20 :                     m = 1;
    8627          20 :                 else if (m == static_cast<int>(m_iYDim) + 1)
    8628          20 :                     m = 2;
    8629             :             }
    8630          20 :             m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
    8631             :         }
    8632          54 :         return m_poSRS.get();
    8633             :     }
    8634             : 
    8635           4 :     CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
    8636             :     {
    8637           4 :         return m_oMDD.SetMetadata(papszMetadata, pszDomain);
    8638             :     }
    8639             : 
    8640         144 :     char **GetMetadata(const char *pszDomain) override
    8641             :     {
    8642         144 :         return m_oMDD.GetMetadata(pszDomain);
    8643             :     }
    8644             : 
    8645         204 :     const char *GetMetadataItem(const char *pszName,
    8646             :                                 const char *pszDomain) override
    8647             :     {
    8648         378 :         if (!m_osOvrFilename.empty() && pszName &&
    8649         390 :             EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
    8650          12 :             EQUAL(pszDomain, "OVERVIEWS"))
    8651             :         {
    8652          12 :             return m_osOvrFilename.c_str();
    8653             :         }
    8654         192 :         return m_oMDD.GetMetadataItem(pszName, pszDomain);
    8655             :     }
    8656             : };
    8657             : 
    8658             : /************************************************************************/
    8659             : /*                      GDALRasterBandFromArray()                       */
    8660             : /************************************************************************/
    8661             : 
    8662         252 : GDALRasterBandFromArray::GDALRasterBandFromArray(
    8663             :     GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
    8664             :     const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
    8665             :     const std::vector<BandImageryMetadata> &aoBandImageryMetadata,
    8666         252 :     double dfDelay, time_t nStartTime, bool &bHasWarned)
    8667             : {
    8668         252 :     const auto &poArray(poDSIn->m_poArray);
    8669         252 :     const auto &dims(poArray->GetDimensions());
    8670         252 :     const auto nDimCount(dims.size());
    8671         504 :     const auto blockSize(poArray->GetBlockSize());
    8672         241 :     nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
    8673         493 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    8674         132 :                                                   blockSize[poDSIn->m_iYDim]))
    8675             :                       : 1;
    8676         252 :     nBlockXSize = blockSize[poDSIn->m_iXDim]
    8677         143 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    8678         143 :                                                   blockSize[poDSIn->m_iXDim]))
    8679         252 :                       : poDSIn->GetRasterXSize();
    8680         252 :     eDataType = poArray->GetDataType().GetNumericDataType();
    8681         252 :     eAccess = poDSIn->eAccess;
    8682         252 :     m_anOffset.resize(nDimCount);
    8683         252 :     m_anCount.resize(nDimCount, 1);
    8684         252 :     m_anStride.resize(nDimCount);
    8685         853 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    8686             :     {
    8687         601 :         if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
    8688             :         {
    8689         216 :             std::string dimName(dims[i]->GetName());
    8690         108 :             GUInt64 nIndex = anOtherDimCoord[j];
    8691             :             // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
    8692             :             // subsetted dimensions as generated by GetView()
    8693         108 :             if (STARTS_WITH(dimName.c_str(), "subset_"))
    8694             :             {
    8695             :                 CPLStringList aosTokens(
    8696          12 :                     CSLTokenizeString2(dimName.c_str(), "_", 0));
    8697           6 :                 if (aosTokens.size() == 5)
    8698             :                 {
    8699           6 :                     dimName = aosTokens[1];
    8700          18 :                     const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
    8701           6 :                         aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
    8702           6 :                     const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
    8703           6 :                     nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
    8704           0 :                                           : nStartDim - (nIndex * -nIncrDim);
    8705             :                 }
    8706             :             }
    8707         108 :             if (nDimCount != 3 || dimName != "Band")
    8708             :             {
    8709          52 :                 SetMetadataItem(
    8710             :                     CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
    8711             :                     CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
    8712             :             }
    8713             : 
    8714         108 :             auto indexingVar = dims[i]->GetIndexingVariable();
    8715             : 
    8716             :             // If the indexing variable is also listed in band parameter arrays,
    8717             :             // then don't use our default formatting
    8718         108 :             if (indexingVar)
    8719             :             {
    8720          38 :                 for (const auto &oItem : aoBandParameterMetadataItems[j])
    8721             :                 {
    8722          12 :                     if (oItem.poArray->GetFullName() ==
    8723          12 :                         indexingVar->GetFullName())
    8724             :                     {
    8725          12 :                         indexingVar.reset();
    8726          12 :                         break;
    8727             :                     }
    8728             :                 }
    8729             :             }
    8730             : 
    8731         134 :             if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
    8732          26 :                 indexingVar->GetDimensions()[0]->GetSize() ==
    8733          26 :                     dims[i]->GetSize())
    8734             :             {
    8735          26 :                 if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
    8736             :                 {
    8737           0 :                     if (!bHasWarned)
    8738             :                     {
    8739           0 :                         CPLError(
    8740             :                             CE_Warning, CPLE_AppDefined,
    8741             :                             "Maximum delay to load band metadata from "
    8742             :                             "dimension indexing variables has expired. "
    8743             :                             "Increase the value of the "
    8744             :                             "LOAD_EXTRA_DIM_METADATA_DELAY "
    8745             :                             "option of GDALMDArray::AsClassicDataset() "
    8746             :                             "(also accessible as the "
    8747             :                             "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
    8748             :                             "configuration option), "
    8749             :                             "or set it to 'unlimited' for unlimited delay. ");
    8750           0 :                         bHasWarned = true;
    8751             :                     }
    8752             :                 }
    8753             :                 else
    8754             :                 {
    8755          26 :                     size_t nCount = 1;
    8756          26 :                     const auto &dt(indexingVar->GetDataType());
    8757          52 :                     std::vector<GByte> abyTmp(dt.GetSize());
    8758          52 :                     if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
    8759          26 :                                           nullptr, nullptr, dt, &abyTmp[0]))
    8760             :                     {
    8761          26 :                         char *pszTmp = nullptr;
    8762          26 :                         GDALExtendedDataType::CopyValue(
    8763          26 :                             &abyTmp[0], dt, &pszTmp,
    8764          52 :                             GDALExtendedDataType::CreateString());
    8765          26 :                         if (pszTmp)
    8766             :                         {
    8767          26 :                             SetMetadataItem(
    8768             :                                 CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
    8769             :                                 pszTmp);
    8770          26 :                             CPLFree(pszTmp);
    8771             :                         }
    8772             : 
    8773          26 :                         const auto &unit(indexingVar->GetUnit());
    8774          26 :                         if (!unit.empty())
    8775             :                         {
    8776          12 :                             SetMetadataItem(
    8777             :                                 CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
    8778             :                                 unit.c_str());
    8779             :                         }
    8780             :                     }
    8781             :                 }
    8782             :             }
    8783             : 
    8784         124 :             for (const auto &oItem : aoBandParameterMetadataItems[j])
    8785             :             {
    8786          32 :                 CPLString osVal;
    8787             : 
    8788          16 :                 size_t nCount = 1;
    8789          16 :                 const auto &dt(oItem.poArray->GetDataType());
    8790          16 :                 if (oItem.bDefinitionUsesPctForG)
    8791             :                 {
    8792             :                     // There is one and only one %[x][.y]f|g in osDefinition
    8793          12 :                     std::vector<GByte> abyTmp(dt.GetSize());
    8794          12 :                     if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
    8795           6 :                                             nullptr, nullptr, dt, &abyTmp[0]))
    8796             :                     {
    8797           6 :                         double dfVal = 0;
    8798           6 :                         GDALExtendedDataType::CopyValue(
    8799           6 :                             &abyTmp[0], dt, &dfVal,
    8800          12 :                             GDALExtendedDataType::Create(GDT_Float64));
    8801           6 :                         osVal.Printf(oItem.osDefinition.c_str(), dfVal);
    8802             :                     }
    8803             :                 }
    8804             :                 else
    8805             :                 {
    8806             :                     // There should be zero or one %s in osDefinition
    8807          10 :                     char *pszValue = nullptr;
    8808          10 :                     if (dt.GetClass() == GEDTC_STRING)
    8809             :                     {
    8810           4 :                         CPL_IGNORE_RET_VAL(oItem.poArray->Read(
    8811           2 :                             &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
    8812             :                             dt, &pszValue));
    8813             :                     }
    8814             :                     else
    8815             :                     {
    8816          16 :                         std::vector<GByte> abyTmp(dt.GetSize());
    8817          16 :                         if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
    8818             :                                                 nullptr, nullptr, dt,
    8819           8 :                                                 &abyTmp[0]))
    8820             :                         {
    8821           8 :                             GDALExtendedDataType::CopyValue(
    8822           8 :                                 &abyTmp[0], dt, &pszValue,
    8823          16 :                                 GDALExtendedDataType::CreateString());
    8824             :                         }
    8825             :                     }
    8826             : 
    8827          10 :                     if (pszValue)
    8828             :                     {
    8829          10 :                         osVal.Printf(oItem.osDefinition.c_str(), pszValue);
    8830          10 :                         CPLFree(pszValue);
    8831             :                     }
    8832             :                 }
    8833          16 :                 if (!osVal.empty())
    8834          16 :                     SetMetadataItem(oItem.osName.c_str(), osVal);
    8835             :             }
    8836             : 
    8837         108 :             if (aoBandImageryMetadata[j].poCentralWavelengthArray)
    8838             :             {
    8839             :                 auto &poCentralWavelengthArray =
    8840           2 :                     aoBandImageryMetadata[j].poCentralWavelengthArray;
    8841           2 :                 size_t nCount = 1;
    8842           2 :                 const auto &dt(poCentralWavelengthArray->GetDataType());
    8843           4 :                 std::vector<GByte> abyTmp(dt.GetSize());
    8844           4 :                 if (poCentralWavelengthArray->Read(&(anOtherDimCoord[j]),
    8845             :                                                    &nCount, nullptr, nullptr,
    8846           2 :                                                    dt, &abyTmp[0]))
    8847             :                 {
    8848           2 :                     double dfVal = 0;
    8849           2 :                     GDALExtendedDataType::CopyValue(
    8850           2 :                         &abyTmp[0], dt, &dfVal,
    8851           4 :                         GDALExtendedDataType::Create(GDT_Float64));
    8852           2 :                     SetMetadataItem(
    8853             :                         "CENTRAL_WAVELENGTH_UM",
    8854             :                         CPLSPrintf(
    8855           2 :                             "%g", dfVal * aoBandImageryMetadata[j]
    8856           2 :                                               .dfCentralWavelengthToMicrometer),
    8857             :                         "IMAGERY");
    8858             :                 }
    8859             :             }
    8860             : 
    8861         108 :             if (aoBandImageryMetadata[j].poFWHMArray)
    8862             :             {
    8863           2 :                 auto &poFWHMArray = aoBandImageryMetadata[j].poFWHMArray;
    8864           2 :                 size_t nCount = 1;
    8865           2 :                 const auto &dt(poFWHMArray->GetDataType());
    8866           4 :                 std::vector<GByte> abyTmp(dt.GetSize());
    8867           4 :                 if (poFWHMArray->Read(&(anOtherDimCoord[j]), &nCount, nullptr,
    8868           2 :                                       nullptr, dt, &abyTmp[0]))
    8869             :                 {
    8870           2 :                     double dfVal = 0;
    8871           2 :                     GDALExtendedDataType::CopyValue(
    8872           2 :                         &abyTmp[0], dt, &dfVal,
    8873           4 :                         GDALExtendedDataType::Create(GDT_Float64));
    8874           2 :                     SetMetadataItem(
    8875             :                         "FWHM_UM",
    8876           2 :                         CPLSPrintf("%g", dfVal * aoBandImageryMetadata[j]
    8877           2 :                                                      .dfFWHMToMicrometer),
    8878             :                         "IMAGERY");
    8879             :                 }
    8880             :             }
    8881             : 
    8882         108 :             m_anOffset[i] = anOtherDimCoord[j];
    8883         108 :             j++;
    8884             :         }
    8885             :     }
    8886         252 : }
    8887             : 
    8888             : /************************************************************************/
    8889             : /*                           GetNoDataValue()                           */
    8890             : /************************************************************************/
    8891             : 
    8892          93 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
    8893             : {
    8894          93 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8895          93 :     const auto &poArray(l_poDS->m_poArray);
    8896          93 :     bool bHasNodata = false;
    8897          93 :     const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
    8898          93 :     if (pbHasNoData)
    8899          81 :         *pbHasNoData = bHasNodata;
    8900          93 :     return res;
    8901             : }
    8902             : 
    8903             : /************************************************************************/
    8904             : /*                       GetNoDataValueAsInt64()                        */
    8905             : /************************************************************************/
    8906             : 
    8907           1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
    8908             : {
    8909           1 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8910           1 :     const auto &poArray(l_poDS->m_poArray);
    8911           1 :     bool bHasNodata = false;
    8912           1 :     const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
    8913           1 :     if (pbHasNoData)
    8914           1 :         *pbHasNoData = bHasNodata;
    8915           1 :     return nodata;
    8916             : }
    8917             : 
    8918             : /************************************************************************/
    8919             : /*                      GetNoDataValueAsUInt64()                        */
    8920             : /************************************************************************/
    8921             : 
    8922           1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
    8923             : {
    8924           1 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8925           1 :     const auto &poArray(l_poDS->m_poArray);
    8926           1 :     bool bHasNodata = false;
    8927           1 :     const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
    8928           1 :     if (pbHasNoData)
    8929           1 :         *pbHasNoData = bHasNodata;
    8930           1 :     return nodata;
    8931             : }
    8932             : 
    8933             : /************************************************************************/
    8934             : /*                             GetOffset()                              */
    8935             : /************************************************************************/
    8936             : 
    8937          29 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
    8938             : {
    8939          29 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8940          29 :     const auto &poArray(l_poDS->m_poArray);
    8941          29 :     bool bHasValue = false;
    8942          29 :     double dfRes = poArray->GetOffset(&bHasValue);
    8943          29 :     if (pbHasOffset)
    8944          17 :         *pbHasOffset = bHasValue;
    8945          29 :     return dfRes;
    8946             : }
    8947             : 
    8948             : /************************************************************************/
    8949             : /*                           GetUnitType()                              */
    8950             : /************************************************************************/
    8951             : 
    8952          36 : const char *GDALRasterBandFromArray::GetUnitType()
    8953             : {
    8954          36 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8955          36 :     const auto &poArray(l_poDS->m_poArray);
    8956          36 :     return poArray->GetUnit().c_str();
    8957             : }
    8958             : 
    8959             : /************************************************************************/
    8960             : /*                             GetScale()                              */
    8961             : /************************************************************************/
    8962             : 
    8963          27 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
    8964             : {
    8965          27 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8966          27 :     const auto &poArray(l_poDS->m_poArray);
    8967          27 :     bool bHasValue = false;
    8968          27 :     double dfRes = poArray->GetScale(&bHasValue);
    8969          27 :     if (pbHasScale)
    8970          15 :         *pbHasScale = bHasValue;
    8971          27 :     return dfRes;
    8972             : }
    8973             : 
    8974             : /************************************************************************/
    8975             : /*                            IReadBlock()                              */
    8976             : /************************************************************************/
    8977             : 
    8978          68 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
    8979             :                                            void *pImage)
    8980             : {
    8981          68 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    8982          68 :     const int nXOff = nBlockXOff * nBlockXSize;
    8983          68 :     const int nYOff = nBlockYOff * nBlockYSize;
    8984          68 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    8985          68 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    8986             :     GDALRasterIOExtraArg sExtraArg;
    8987          68 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    8988         136 :     return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    8989             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    8990         136 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    8991             : }
    8992             : 
    8993             : /************************************************************************/
    8994             : /*                            IWriteBlock()                             */
    8995             : /************************************************************************/
    8996             : 
    8997           0 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
    8998             :                                             void *pImage)
    8999             : {
    9000           0 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    9001           0 :     const int nXOff = nBlockXOff * nBlockXSize;
    9002           0 :     const int nYOff = nBlockYOff * nBlockYSize;
    9003           0 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    9004           0 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    9005             :     GDALRasterIOExtraArg sExtraArg;
    9006           0 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    9007           0 :     return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    9008             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    9009           0 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    9010             : }
    9011             : 
    9012             : /************************************************************************/
    9013             : /*                            IRasterIO()                               */
    9014             : /************************************************************************/
    9015             : 
    9016         320 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
    9017             :                                           int nYOff, int nXSize, int nYSize,
    9018             :                                           void *pData, int nBufXSize,
    9019             :                                           int nBufYSize, GDALDataType eBufType,
    9020             :                                           GSpacing nPixelSpaceBuf,
    9021             :                                           GSpacing nLineSpaceBuf,
    9022             :                                           GDALRasterIOExtraArg *psExtraArg)
    9023             : {
    9024         320 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9025         320 :     const auto &poArray(l_poDS->m_poArray);
    9026         320 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
    9027         320 :     if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
    9028         320 :         (nPixelSpaceBuf % nBufferDTSize) == 0 &&
    9029         320 :         (nLineSpaceBuf % nBufferDTSize) == 0)
    9030             :     {
    9031         320 :         m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
    9032         320 :         m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
    9033         640 :         m_anStride[l_poDS->m_iXDim] =
    9034         320 :             static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
    9035         320 :         if (poArray->GetDimensionCount() >= 2)
    9036             :         {
    9037         311 :             m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
    9038         311 :             m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
    9039         311 :             m_anStride[l_poDS->m_iYDim] =
    9040         311 :                 static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
    9041             :         }
    9042         320 :         if (eRWFlag == GF_Read)
    9043             :         {
    9044         632 :             return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
    9045         316 :                                  m_anStride.data(),
    9046         632 :                                  GDALExtendedDataType::Create(eBufType), pData)
    9047         316 :                        ? CE_None
    9048         316 :                        : CE_Failure;
    9049             :         }
    9050             :         else
    9051             :         {
    9052           8 :             return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
    9053           4 :                                   m_anStride.data(),
    9054           8 :                                   GDALExtendedDataType::Create(eBufType), pData)
    9055           4 :                        ? CE_None
    9056           4 :                        : CE_Failure;
    9057             :         }
    9058             :     }
    9059           0 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    9060             :                                      pData, nBufXSize, nBufYSize, eBufType,
    9061           0 :                                      nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
    9062             : }
    9063             : 
    9064             : /************************************************************************/
    9065             : /*                      GetColorInterpretation()                        */
    9066             : /************************************************************************/
    9067             : 
    9068          45 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
    9069             : {
    9070          45 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    9071          45 :     const auto &poArray(l_poDS->m_poArray);
    9072         135 :     auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
    9073          45 :     if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
    9074             :     {
    9075           6 :         bool bOK = false;
    9076           6 :         GUInt64 nStartIndex = 0;
    9077           6 :         if (poArray->GetDimensionCount() == 2 &&
    9078           0 :             poAttr->GetDimensionCount() == 0)
    9079             :         {
    9080           0 :             bOK = true;
    9081             :         }
    9082           6 :         else if (poArray->GetDimensionCount() == 3)
    9083             :         {
    9084           6 :             uint64_t nExtraDimSamples = 1;
    9085           6 :             const auto &apoDims = poArray->GetDimensions();
    9086          24 :             for (size_t i = 0; i < apoDims.size(); ++i)
    9087             :             {
    9088          18 :                 if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
    9089           6 :                     nExtraDimSamples *= apoDims[i]->GetSize();
    9090             :             }
    9091           6 :             if (poAttr->GetDimensionsSize() ==
    9092          12 :                 std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
    9093             :             {
    9094           6 :                 bOK = true;
    9095             :             }
    9096           6 :             nStartIndex = nBand - 1;
    9097             :         }
    9098           6 :         if (bOK)
    9099             :         {
    9100           6 :             const auto oStringDT = GDALExtendedDataType::CreateString();
    9101           6 :             const size_t nCount = 1;
    9102           6 :             const GInt64 arrayStep = 1;
    9103           6 :             const GPtrDiff_t bufferStride = 1;
    9104           6 :             char *pszValue = nullptr;
    9105           6 :             poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
    9106           6 :                          oStringDT, &pszValue);
    9107           6 :             if (pszValue)
    9108             :             {
    9109             :                 const auto eColorInterp =
    9110           6 :                     GDALGetColorInterpretationByName(pszValue);
    9111           6 :                 CPLFree(pszValue);
    9112           6 :                 return eColorInterp;
    9113             :             }
    9114             :         }
    9115             :     }
    9116          39 :     return GCI_Undefined;
    9117             : }
    9118             : 
    9119             : /************************************************************************/
    9120             : /*                    GDALDatasetFromArray::Create()                    */
    9121             : /************************************************************************/
    9122             : 
    9123         223 : GDALDatasetFromArray *GDALDatasetFromArray::Create(
    9124             :     const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
    9125             :     const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
    9126             : 
    9127             : {
    9128         223 :     const auto nDimCount(array->GetDimensionCount());
    9129         223 :     if (nDimCount == 0)
    9130             :     {
    9131           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    9132             :                  "Unsupported number of dimensions");
    9133           1 :         return nullptr;
    9134             :     }
    9135         443 :     if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
    9136         221 :         array->GetDataType().GetNumericDataType() == GDT_Unknown)
    9137             :     {
    9138           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    9139             :                  "Only arrays with numeric data types "
    9140             :                  "can be exposed as classic GDALDataset");
    9141           1 :         return nullptr;
    9142             :     }
    9143         221 :     if (iXDim >= nDimCount ||
    9144         207 :         (nDimCount >= 2 && (iYDim >= nDimCount || iXDim == iYDim)))
    9145             :     {
    9146           6 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
    9147           6 :         return nullptr;
    9148             :     }
    9149         215 :     GUInt64 nTotalBands = 1;
    9150         215 :     const auto &dims(array->GetDimensions());
    9151         697 :     for (size_t i = 0; i < nDimCount; ++i)
    9152             :     {
    9153         483 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9154             :         {
    9155          66 :             if (dims[i]->GetSize() > 65536 / nTotalBands)
    9156             :             {
    9157           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9158             :                          "Too many bands. Operate on a sliced view");
    9159           1 :                 return nullptr;
    9160             :             }
    9161          65 :             nTotalBands *= dims[i]->GetSize();
    9162             :         }
    9163             :     }
    9164             : 
    9165         428 :     std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
    9166         696 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    9167             :     {
    9168         482 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9169             :         {
    9170          65 :             oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
    9171          65 :             ++j;
    9172             :         }
    9173             :     }
    9174             : 
    9175         214 :     const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
    9176             : 
    9177             :     const char *pszBandMetadata =
    9178         214 :         CSLFetchNameValue(papszOptions, "BAND_METADATA");
    9179             :     std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
    9180         428 :         nNewDimCount);
    9181         214 :     if (pszBandMetadata)
    9182             :     {
    9183          21 :         if (!poRootGroup)
    9184             :         {
    9185           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9186             :                      "Root group should be provided when BAND_METADATA is set");
    9187          14 :             return nullptr;
    9188             :         }
    9189          20 :         CPLJSONDocument oDoc;
    9190          20 :         if (!oDoc.LoadMemory(pszBandMetadata))
    9191             :         {
    9192           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9193             :                      "Invalid JSON content for BAND_METADATA");
    9194           1 :             return nullptr;
    9195             :         }
    9196          19 :         auto oRoot = oDoc.GetRoot();
    9197          19 :         if (oRoot.GetType() != CPLJSONObject::Type::Array)
    9198             :         {
    9199           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9200             :                      "Value of BAND_METADATA should be an array");
    9201           1 :             return nullptr;
    9202             :         }
    9203             : 
    9204          18 :         auto oArray = oRoot.ToArray();
    9205          26 :         for (int j = 0; j < oArray.Size(); ++j)
    9206             :         {
    9207          19 :             const auto oJsonItem = oArray[j];
    9208          19 :             MetadataItem oItem;
    9209             : 
    9210          38 :             auto osBandArrayFullname = oJsonItem.GetString("array");
    9211          19 :             if (osBandArrayFullname.empty())
    9212             :             {
    9213           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9214             :                          "BAND_METADATA[%d][\"array\"] is missing", j);
    9215           1 :                 return nullptr;
    9216             :             }
    9217             :             oItem.poArray =
    9218          18 :                 poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
    9219          18 :             if (!oItem.poArray)
    9220             :             {
    9221           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9222             :                          "Array %s cannot be found",
    9223             :                          osBandArrayFullname.c_str());
    9224           1 :                 return nullptr;
    9225             :             }
    9226          17 :             if (oItem.poArray->GetDimensionCount() != 1)
    9227             :             {
    9228           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9229             :                          "Array %s is not a 1D array",
    9230             :                          osBandArrayFullname.c_str());
    9231           1 :                 return nullptr;
    9232             :             }
    9233             :             const auto &osAuxArrayDimName =
    9234          16 :                 oItem.poArray->GetDimensions()[0]->GetName();
    9235          16 :             auto oIter = oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
    9236          16 :             if (oIter == oMapArrayDimNameToExtraDimIdx.end())
    9237             :             {
    9238           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9239             :                          "Dimension %s of array %s is not a non-X/Y dimension "
    9240             :                          "of array %s",
    9241             :                          osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
    9242           1 :                          array->GetName().c_str());
    9243           1 :                 return nullptr;
    9244             :             }
    9245          15 :             const size_t iExtraDimIdx = oIter->second;
    9246          15 :             CPLAssert(iExtraDimIdx < nNewDimCount);
    9247             : 
    9248          15 :             oItem.osName = oJsonItem.GetString("item_name");
    9249          15 :             if (oItem.osName.empty())
    9250             :             {
    9251           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9252             :                          "BAND_METADATA[%d][\"item_name\"] is missing", j);
    9253           1 :                 return nullptr;
    9254             :             }
    9255             : 
    9256          28 :             const auto osDefinition = oJsonItem.GetString("item_value", "%s");
    9257             : 
    9258             :             // Check correctness of definition
    9259          14 :             bool bFirstNumericFormatter = true;
    9260          14 :             std::string osModDefinition;
    9261          14 :             bool bDefinitionUsesPctForG = false;
    9262          72 :             for (size_t k = 0; k < osDefinition.size(); ++k)
    9263             :             {
    9264          64 :                 if (osDefinition[k] == '%')
    9265             :                 {
    9266          13 :                     osModDefinition += osDefinition[k];
    9267          13 :                     if (k + 1 == osDefinition.size())
    9268             :                     {
    9269           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9270             :                                  "Value of "
    9271             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9272             :                                  "%s is invalid at offset %d",
    9273             :                                  osAuxArrayDimName.c_str(), j,
    9274             :                                  osDefinition.c_str(), int(k));
    9275           1 :                         return nullptr;
    9276             :                     }
    9277          12 :                     ++k;
    9278          12 :                     if (osDefinition[k] == '%')
    9279             :                     {
    9280           1 :                         osModDefinition += osDefinition[k];
    9281           1 :                         continue;
    9282             :                     }
    9283          11 :                     if (!bFirstNumericFormatter)
    9284             :                     {
    9285           1 :                         CPLError(
    9286             :                             CE_Failure, CPLE_AppDefined,
    9287             :                             "Value of "
    9288             :                             "BAND_METADATA[\"%s\"][%d][\"item_value\"] = %s is "
    9289             :                             "invalid at offset %d: %%[x][.y]f|g or %%s "
    9290             :                             "formatters should be specified at most once",
    9291             :                             osAuxArrayDimName.c_str(), j, osDefinition.c_str(),
    9292             :                             int(k));
    9293           1 :                         return nullptr;
    9294             :                     }
    9295          10 :                     bFirstNumericFormatter = false;
    9296          13 :                     for (; k < osDefinition.size(); ++k)
    9297             :                     {
    9298          13 :                         osModDefinition += osDefinition[k];
    9299          26 :                         if (!((osDefinition[k] >= '0' &&
    9300          12 :                                osDefinition[k] <= '9') ||
    9301          11 :                               osDefinition[k] == '.'))
    9302          10 :                             break;
    9303             :                     }
    9304          20 :                     if (k == osDefinition.size() ||
    9305          10 :                         (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
    9306           5 :                          osDefinition[k] != 's'))
    9307             :                     {
    9308           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9309             :                                  "Value of "
    9310             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9311             :                                  "%s is invalid at offset %d: only "
    9312             :                                  "%%[x][.y]f|g or %%s formatters are accepted",
    9313             :                                  osAuxArrayDimName.c_str(), j,
    9314             :                                  osDefinition.c_str(), int(k));
    9315           1 :                         return nullptr;
    9316             :                     }
    9317           9 :                     bDefinitionUsesPctForG =
    9318           9 :                         (osDefinition[k] == 'f' || osDefinition[k] == 'g');
    9319           9 :                     if (bDefinitionUsesPctForG)
    9320             :                     {
    9321           5 :                         if (oItem.poArray->GetDataType().GetClass() !=
    9322             :                             GEDTC_NUMERIC)
    9323             :                         {
    9324           1 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9325             :                                      "Data type of %s array is not numeric",
    9326             :                                      osAuxArrayDimName.c_str());
    9327           1 :                             return nullptr;
    9328             :                         }
    9329             :                     }
    9330             :                 }
    9331          56 :                 else if (osDefinition[k] == '$' &&
    9332          56 :                          k + 1 < osDefinition.size() &&
    9333           5 :                          osDefinition[k + 1] == '{')
    9334             :                 {
    9335           5 :                     const auto nPos = osDefinition.find('}', k);
    9336           5 :                     if (nPos == std::string::npos)
    9337             :                     {
    9338           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9339             :                                  "Value of "
    9340             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9341             :                                  "%s is invalid at offset %d",
    9342             :                                  osAuxArrayDimName.c_str(), j,
    9343             :                                  osDefinition.c_str(), int(k));
    9344           2 :                         return nullptr;
    9345             :                     }
    9346             :                     const auto osAttrName =
    9347           4 :                         osDefinition.substr(k + 2, nPos - (k + 2));
    9348           4 :                     auto poAttr = oItem.poArray->GetAttribute(osAttrName);
    9349           4 :                     if (!poAttr)
    9350             :                     {
    9351           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9352             :                                  "Value of "
    9353             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9354             :                                  "%s is invalid: %s is not an attribute of %s",
    9355             :                                  osAuxArrayDimName.c_str(), j,
    9356             :                                  osDefinition.c_str(), osAttrName.c_str(),
    9357             :                                  osAuxArrayDimName.c_str());
    9358           1 :                         return nullptr;
    9359             :                     }
    9360           3 :                     k = nPos;
    9361           3 :                     const char *pszValue = poAttr->ReadAsString();
    9362           3 :                     if (!pszValue)
    9363             :                     {
    9364           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9365             :                                  "Cannot get value of attribute %s of %s as a "
    9366             :                                  "string",
    9367             :                                  osAttrName.c_str(), osAuxArrayDimName.c_str());
    9368           0 :                         return nullptr;
    9369             :                     }
    9370           3 :                     osModDefinition += pszValue;
    9371             :                 }
    9372             :                 else
    9373             :                 {
    9374          46 :                     osModDefinition += osDefinition[k];
    9375             :                 }
    9376             :             }
    9377             : 
    9378           8 :             oItem.osDefinition = std::move(osModDefinition);
    9379           8 :             oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
    9380             : 
    9381           8 :             aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
    9382           8 :                 std::move(oItem));
    9383             :         }
    9384             :     }
    9385             : 
    9386         400 :     std::vector<BandImageryMetadata> aoBandImageryMetadata(nNewDimCount);
    9387             :     const char *pszBandImageryMetadata =
    9388         200 :         CSLFetchNameValue(papszOptions, "BAND_IMAGERY_METADATA");
    9389         200 :     if (pszBandImageryMetadata)
    9390             :     {
    9391          12 :         if (!poRootGroup)
    9392             :         {
    9393           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9394             :                      "Root group should be provided when BAND_IMAGERY_METADATA "
    9395             :                      "is set");
    9396          10 :             return nullptr;
    9397             :         }
    9398          11 :         CPLJSONDocument oDoc;
    9399          11 :         if (!oDoc.LoadMemory(pszBandImageryMetadata))
    9400             :         {
    9401           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9402             :                      "Invalid JSON content for BAND_IMAGERY_METADATA");
    9403           1 :             return nullptr;
    9404             :         }
    9405          10 :         auto oRoot = oDoc.GetRoot();
    9406          10 :         if (oRoot.GetType() != CPLJSONObject::Type::Object)
    9407             :         {
    9408           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9409             :                      "Value of BAND_IMAGERY_METADATA should be an object");
    9410           1 :             return nullptr;
    9411             :         }
    9412          12 :         for (const auto &oJsonItem : oRoot.GetChildren())
    9413             :         {
    9414          22 :             if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM" ||
    9415          12 :                 oJsonItem.GetName() == "FWHM_UM")
    9416             :             {
    9417          18 :                 auto osBandArrayFullname = oJsonItem.GetString("array");
    9418           9 :                 if (osBandArrayFullname.empty())
    9419             :                 {
    9420           1 :                     CPLError(
    9421             :                         CE_Failure, CPLE_AppDefined,
    9422             :                         "BAND_IMAGERY_METADATA[\"%s\"][\"array\"] is missing",
    9423           2 :                         oJsonItem.GetName().c_str());
    9424           1 :                     return nullptr;
    9425             :                 }
    9426             :                 auto poArray =
    9427           8 :                     poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
    9428           8 :                 if (!poArray)
    9429             :                 {
    9430           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9431             :                              "Array %s cannot be found",
    9432             :                              osBandArrayFullname.c_str());
    9433           1 :                     return nullptr;
    9434             :                 }
    9435           7 :                 if (poArray->GetDimensionCount() != 1)
    9436             :                 {
    9437           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9438             :                              "Array %s is not a 1D array",
    9439             :                              osBandArrayFullname.c_str());
    9440           1 :                     return nullptr;
    9441             :                 }
    9442             :                 const auto &osAuxArrayDimName =
    9443           6 :                     poArray->GetDimensions()[0]->GetName();
    9444             :                 auto oIter =
    9445           6 :                     oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
    9446           6 :                 if (oIter == oMapArrayDimNameToExtraDimIdx.end())
    9447             :                 {
    9448           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9449             :                              "Dimension \"%s\" of array \"%s\" is not a "
    9450             :                              "non-X/Y dimension of array \"%s\"",
    9451             :                              osAuxArrayDimName.c_str(),
    9452             :                              osBandArrayFullname.c_str(),
    9453           1 :                              array->GetName().c_str());
    9454           1 :                     return nullptr;
    9455             :                 }
    9456           5 :                 const size_t iExtraDimIdx = oIter->second;
    9457           5 :                 CPLAssert(iExtraDimIdx < nNewDimCount);
    9458             : 
    9459          10 :                 std::string osUnit = oJsonItem.GetString("unit", "um");
    9460           5 :                 if (STARTS_WITH(osUnit.c_str(), "${"))
    9461             :                 {
    9462           3 :                     if (osUnit.back() != '}')
    9463             :                     {
    9464           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9465             :                                  "Value of "
    9466             :                                  "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
    9467             :                                  "%s is invalid",
    9468           2 :                                  oJsonItem.GetName().c_str(), osUnit.c_str());
    9469           2 :                         return nullptr;
    9470             :                     }
    9471           2 :                     const auto osAttrName = osUnit.substr(2, osUnit.size() - 3);
    9472           2 :                     auto poAttr = poArray->GetAttribute(osAttrName);
    9473           2 :                     if (!poAttr)
    9474             :                     {
    9475           2 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9476             :                                  "Value of "
    9477             :                                  "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = "
    9478             :                                  "%s is invalid: %s is not an attribute of %s",
    9479           2 :                                  oJsonItem.GetName().c_str(), osUnit.c_str(),
    9480             :                                  osAttrName.c_str(),
    9481             :                                  osBandArrayFullname.c_str());
    9482           1 :                         return nullptr;
    9483             :                     }
    9484           1 :                     const char *pszValue = poAttr->ReadAsString();
    9485           1 :                     if (!pszValue)
    9486             :                     {
    9487           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9488             :                                  "Cannot get value of attribute %s of %s as a "
    9489             :                                  "string",
    9490             :                                  osAttrName.c_str(),
    9491             :                                  osBandArrayFullname.c_str());
    9492           0 :                         return nullptr;
    9493             :                     }
    9494           1 :                     osUnit = pszValue;
    9495             :                 }
    9496           3 :                 double dfConvToUM = 1.0;
    9497           7 :                 if (osUnit == "nm" || osUnit == "nanometre" ||
    9498           9 :                     osUnit == "nanometres" || osUnit == "nanometer" ||
    9499           2 :                     osUnit == "nanometers")
    9500             :                 {
    9501           1 :                     dfConvToUM = 1e-3;
    9502             :                 }
    9503           3 :                 else if (!(osUnit == "um" || osUnit == "micrometre" ||
    9504           1 :                            osUnit == "micrometres" || osUnit == "micrometer" ||
    9505           1 :                            osUnit == "micrometers"))
    9506             :                 {
    9507           2 :                     CPLError(CE_Failure, CPLE_AppDefined,
    9508             :                              "Unhandled value for "
    9509             :                              "BAND_IMAGERY_METADATA[\"%s\"][\"unit\"] = %s",
    9510           2 :                              oJsonItem.GetName().c_str(), osUnit.c_str());
    9511           1 :                     return nullptr;
    9512             :                 }
    9513             : 
    9514           2 :                 BandImageryMetadata &item = aoBandImageryMetadata[iExtraDimIdx];
    9515           2 :                 if (oJsonItem.GetName() == "CENTRAL_WAVELENGTH_UM")
    9516             :                 {
    9517           1 :                     item.poCentralWavelengthArray = std::move(poArray);
    9518           1 :                     item.dfCentralWavelengthToMicrometer = dfConvToUM;
    9519             :                 }
    9520             :                 else
    9521             :                 {
    9522           1 :                     item.poFWHMArray = std::move(poArray);
    9523           1 :                     item.dfFWHMToMicrometer = dfConvToUM;
    9524             :                 }
    9525             :             }
    9526             :             else
    9527             :             {
    9528           1 :                 CPLError(CE_Warning, CPLE_AppDefined,
    9529             :                          "Ignored member \"%s\" in BAND_IMAGERY_METADATA",
    9530           2 :                          oJsonItem.GetName().c_str());
    9531             :             }
    9532             :         }
    9533             :     }
    9534             : 
    9535         380 :     auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
    9536             : 
    9537         190 :     poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
    9538             : 
    9539         190 :     poDS->nRasterYSize =
    9540         190 :         nDimCount < 2 ? 1
    9541         179 :                       : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    9542         179 :                                                   dims[iYDim]->GetSize()));
    9543         380 :     poDS->nRasterXSize = static_cast<int>(
    9544         190 :         std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
    9545             : 
    9546         380 :     std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
    9547         380 :     std::vector<GUInt64> anStackIters(nDimCount);
    9548         380 :     std::vector<size_t> anMapNewToOld(nNewDimCount);
    9549         600 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    9550             :     {
    9551         410 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9552             :         {
    9553          41 :             anMapNewToOld[j] = i;
    9554          41 :             j++;
    9555             :         }
    9556             :     }
    9557             : 
    9558         380 :     poDS->m_bHasGT =
    9559         190 :         array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_adfGeoTransform);
    9560             : 
    9561         380 :     const auto attrs(array->GetAttributes());
    9562         268 :     for (const auto &attr : attrs)
    9563             :     {
    9564          78 :         if (attr->GetName() != "COLOR_INTERPRETATION")
    9565             :         {
    9566         144 :             auto stringArray = attr->ReadAsStringArray();
    9567         144 :             std::string val;
    9568          72 :             if (stringArray.size() > 1)
    9569             :             {
    9570          22 :                 val += '{';
    9571             :             }
    9572         166 :             for (int i = 0; i < stringArray.size(); ++i)
    9573             :             {
    9574          94 :                 if (i > 0)
    9575          22 :                     val += ',';
    9576          94 :                 val += stringArray[i];
    9577             :             }
    9578          72 :             if (stringArray.size() > 1)
    9579             :             {
    9580          22 :                 val += '}';
    9581             :             }
    9582          72 :             poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
    9583             :         }
    9584             :     }
    9585             : 
    9586         190 :     const char *pszDelay = CSLFetchNameValueDef(
    9587             :         papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
    9588             :         CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
    9589             :     const double dfDelay =
    9590         190 :         EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
    9591         190 :     const auto nStartTime = time(nullptr);
    9592         190 :     bool bHasWarned = false;
    9593             :     // Instantiate bands by iterating over non-XY variables
    9594         190 :     size_t iDim = 0;
    9595         190 :     int nCurBand = 1;
    9596         295 : lbl_next_depth:
    9597         295 :     if (iDim < nNewDimCount)
    9598             :     {
    9599          43 :         anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
    9600          43 :         anOtherDimCoord[iDim] = 0;
    9601             :         while (true)
    9602             :         {
    9603         105 :             ++iDim;
    9604         105 :             goto lbl_next_depth;
    9605         105 :         lbl_return_to_caller:
    9606         105 :             --iDim;
    9607         105 :             --anStackIters[iDim];
    9608         105 :             if (anStackIters[iDim] == 0)
    9609          43 :                 break;
    9610          62 :             ++anOtherDimCoord[iDim];
    9611             :         }
    9612             :     }
    9613             :     else
    9614             :     {
    9615         504 :         poDS->SetBand(nCurBand,
    9616             :                       new GDALRasterBandFromArray(
    9617         252 :                           poDS.get(), anOtherDimCoord,
    9618             :                           aoBandParameterMetadataItems, aoBandImageryMetadata,
    9619         252 :                           dfDelay, nStartTime, bHasWarned));
    9620         252 :         ++nCurBand;
    9621             :     }
    9622         295 :     if (iDim > 0)
    9623         105 :         goto lbl_return_to_caller;
    9624             : 
    9625         190 :     if (!array->GetFilename().empty())
    9626             :     {
    9627         169 :         poDS->SetPhysicalFilename(array->GetFilename().c_str());
    9628             :         std::string osDerivedDatasetName(
    9629             :             CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
    9630         338 :                        int(iYDim), array->GetFullName().c_str()));
    9631         169 :         if (!array->GetContext().empty())
    9632             :         {
    9633           2 :             osDerivedDatasetName += " with context ";
    9634           2 :             osDerivedDatasetName += array->GetContext();
    9635             :         }
    9636         169 :         poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
    9637         169 :         poDS->TryLoadXML();
    9638             : 
    9639           2 :         for (const auto &[pszKey, pszValue] :
    9640             :              cpl::IterateNameValue(static_cast<CSLConstList>(
    9641         171 :                  poDS->GDALPamDataset::GetMetadata())))
    9642             :         {
    9643           1 :             poDS->m_oMDD.SetMetadataItem(pszKey, pszValue);
    9644             :         }
    9645             :     }
    9646             : 
    9647         190 :     return poDS.release();
    9648             : }
    9649             : 
    9650             : /************************************************************************/
    9651             : /*                          AsClassicDataset()                         */
    9652             : /************************************************************************/
    9653             : 
    9654             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
    9655             :  *
    9656             :  * In the case of > 2D arrays, additional dimensions will be represented as
    9657             :  * raster bands.
    9658             :  *
    9659             :  * The "reverse" method is GDALRasterBand::AsMDArray().
    9660             :  *
    9661             :  * This is the same as the C function GDALMDArrayAsClassicDataset().
    9662             :  *
    9663             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
    9664             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
    9665             :  *              Ignored if the dimension count is 1.
    9666             :  * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
    9667             :  *                    and BAND_IMAGERY_METADATA option.
    9668             :  * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
    9669             :  *                     nullptr. Current supported options are:
    9670             :  *                     <ul>
    9671             :  *                     <li>BAND_METADATA: JSON serialized array defining which
    9672             :  *                         arrays of the poRootGroup, indexed by non-X and Y
    9673             :  *                         dimensions, should be mapped as band metadata items.
    9674             :  *                         Each array item should be an object with the
    9675             :  *                         following members:
    9676             :  *                         - "array": full name of a band parameter array.
    9677             :  *                           Such array must be a one
    9678             :  *                           dimensional array, and its dimension must be one of
    9679             :  *                           the dimensions of the array on which the method is
    9680             :  *                           called (excluding the X and Y dimensons).
    9681             :  *                         - "item_name": band metadata item name
    9682             :  *                         - "item_value": (optional) String, where "%[x][.y]f",
    9683             :  *                           "%[x][.y]g" or "%s" printf-like formatting can be
    9684             :  *                           used to format the corresponding value of the
    9685             :  *                           parameter array. The percentage character should be
    9686             :  *                           repeated: "%%"
    9687             :  *                           "${attribute_name}" can also be used to include the
    9688             :  *                           value of an attribute for the array.
    9689             :  *                           If "item_value" is not provided, a default formatting
    9690             :  *                           of the value will be applied.
    9691             :  *
    9692             :  *                         Example:
    9693             :  *                         [
    9694             :  *                            {
    9695             :  *                              "array": "/sensor_band_parameters/wavelengths",
    9696             :  *                              "item_name": "WAVELENGTH",
    9697             :  *                              "item_value": "%.1f ${units}"
    9698             :  *                            },
    9699             :  *                            {
    9700             :  *                              "array": "/sensor_band_parameters/fwhm",
    9701             :  *                              "item_name": "FWHM"
    9702             :  *                            },
    9703             :  *                            {
    9704             :  *                              "array": "/sensor_band_parameters/fwhm",
    9705             :  *                              "item_name": "FWHM_UNIT",
    9706             :  *                              "item_value": "${units}"
    9707             :  *                            }
    9708             :  *                         ]
    9709             :  *                     </li>
    9710             :  *                     <li>BAND_IMAGERY_METADATA: (GDAL >= 3.11)
    9711             :  *                         JSON serialized object defining which arrays of the
    9712             :  *                         poRootGroup, indexed by non-X and Y dimensions,
    9713             :  *                         should be mapped as band metadata items in the
    9714             :  *                         band IMAGERY domain.
    9715             :  *                         The object currently accepts 2 members:
    9716             :  *                         - "CENTRAL_WAVELENGTH_UM": Central Wavelength in
    9717             :  *                           micrometers.
    9718             :  *                         - "FWHM_UM": Full-width half-maximum
    9719             :  *                           in micrometers.
    9720             :  *                         The value of each member should be an object with the
    9721             :  *                         following members:
    9722             :  *                         - "array": (required) full name of a band parameter
    9723             :  *                           array.
    9724             :  *                           Such array must be a one dimensional array, and its
    9725             :  *                           dimension must be one of the dimensions of the
    9726             :  *                           array on which the method is called
    9727             :  *                           (excluding the X and Y dimensons).
    9728             :  *                         - "unit": (optional) unit of the values pointed in
    9729             :  *                           the above array.
    9730             :  *                           Can be a literal string or a string of the form
    9731             :  *                           "${attribute_name}" to point to an attribute for
    9732             :  *                           the array.
    9733             :  *                           Accepted values are "um", "micrometer"
    9734             :  *                           (with UK vs US spelling, singular or plural), "nm",
    9735             :  *                           "nanometer" (with UK vs US spelling, singular or
    9736             :  *                           plural)
    9737             :  *                           If not provided, micrometer is assumed.
    9738             :  *
    9739             :  *                         Example for EMIT datasets:
    9740             :  *                         {
    9741             :  *                            "CENTRAL_WAVELENGTH_UM": {
    9742             :  *                                "array": "/sensor_band_parameters/wavelengths",
    9743             :  *                                "unit": "${units}"
    9744             :  *                            },
    9745             :  *                            "FWHM_UM": {
    9746             :  *                                "array": "/sensor_band_parameters/fwhm",
    9747             :  *                                "unit": "${units}"
    9748             :  *                            }
    9749             :  *                         }
    9750             :  *                     </li>
    9751             :  *                     <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
    9752             :  *                         seconds allowed to set the DIM_{dimname}_VALUE band
    9753             :  *                         metadata items from the indexing variable of the
    9754             :  *                         dimensions.
    9755             :  *                         Default value is 5. 'unlimited' can be used to mean
    9756             :  *                         unlimited delay. Can also be defined globally with
    9757             :  *                         the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
    9758             :  *                         option.</li>
    9759             :  *                     </ul>
    9760             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
    9761             :  */
    9762             : GDALDataset *
    9763         223 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
    9764             :                               const std::shared_ptr<GDALGroup> &poRootGroup,
    9765             :                               CSLConstList papszOptions) const
    9766             : {
    9767         446 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    9768         223 :     if (!self)
    9769             :     {
    9770           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    9771             :                  "Driver implementation issue: m_pSelf not set !");
    9772           0 :         return nullptr;
    9773             :     }
    9774         223 :     return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
    9775         223 :                                         papszOptions);
    9776             : }
    9777             : 
    9778             : /************************************************************************/
    9779             : /*                           GetStatistics()                            */
    9780             : /************************************************************************/
    9781             : 
    9782             : /**
    9783             :  * \brief Fetch statistics.
    9784             :  *
    9785             :  * Returns the minimum, maximum, mean and standard deviation of all
    9786             :  * pixel values in this array.
    9787             :  *
    9788             :  * If bForce is FALSE results will only be returned if it can be done
    9789             :  * quickly (i.e. without scanning the data).  If bForce is FALSE and
    9790             :  * results cannot be returned efficiently, the method will return CE_Warning
    9791             :  * but no warning will have been issued.   This is a non-standard use of
    9792             :  * the CE_Warning return value to indicate "nothing done".
    9793             :  *
    9794             :  * When cached statistics are not available, and bForce is TRUE,
    9795             :  * ComputeStatistics() is called.
    9796             :  *
    9797             :  * Note that file formats using PAM (Persistent Auxiliary Metadata) services
    9798             :  * will generally cache statistics in the .aux.xml file allowing fast fetch
    9799             :  * after the first request.
    9800             :  *
    9801             :  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
    9802             :  *
    9803             :  * This method is the same as the C function GDALMDArrayGetStatistics().
    9804             :  *
    9805             :  * @param bApproxOK Currently ignored. In the future, should be set to true
    9806             :  * if statistics on the whole array are wished, or to false if a subset of it
    9807             :  * may be used.
    9808             :  *
    9809             :  * @param bForce If false statistics will only be returned if it can
    9810             :  * be done without rescanning the image.
    9811             :  *
    9812             :  * @param pdfMin Location into which to load image minimum (may be NULL).
    9813             :  *
    9814             :  * @param pdfMax Location into which to load image maximum (may be NULL).-
    9815             :  *
    9816             :  * @param pdfMean Location into which to load image mean (may be NULL).
    9817             :  *
    9818             :  * @param pdfStdDev Location into which to load image standard deviation
    9819             :  * (may be NULL).
    9820             :  *
    9821             :  * @param pnValidCount Number of samples whose value is different from the
    9822             :  * nodata value. (may be NULL)
    9823             :  *
    9824             :  * @param pfnProgress a function to call to report progress, or NULL.
    9825             :  *
    9826             :  * @param pProgressData application data to pass to the progress function.
    9827             :  *
    9828             :  * @return CE_None on success, CE_Warning if no values returned,
    9829             :  * CE_Failure if an error occurs.
    9830             :  *
    9831             :  * @since GDAL 3.2
    9832             :  */
    9833             : 
    9834           7 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
    9835             :                                   double *pdfMax, double *pdfMean,
    9836             :                                   double *pdfStdDev, GUInt64 *pnValidCount,
    9837             :                                   GDALProgressFunc pfnProgress,
    9838             :                                   void *pProgressData)
    9839             : {
    9840           7 :     if (!bForce)
    9841           1 :         return CE_Warning;
    9842             : 
    9843          12 :     return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
    9844           6 :                              pnValidCount, pfnProgress, pProgressData, nullptr)
    9845           6 :                ? CE_None
    9846           6 :                : CE_Failure;
    9847             : }
    9848             : 
    9849             : /************************************************************************/
    9850             : /*                         ComputeStatistics()                          */
    9851             : /************************************************************************/
    9852             : 
    9853             : /**
    9854             :  * \brief Compute statistics.
    9855             :  *
    9856             :  * Returns the minimum, maximum, mean and standard deviation of all
    9857             :  * pixel values in this array.
    9858             :  *
    9859             :  * Pixels taken into account in statistics are those whose mask value
    9860             :  * (as determined by GetMask()) is non-zero.
    9861             :  *
    9862             :  * Once computed, the statistics will generally be "set" back on the
    9863             :  * owing dataset.
    9864             :  *
    9865             :  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
    9866             :  *
    9867             :  * This method is the same as the C functions GDALMDArrayComputeStatistics().
    9868             :  * and GDALMDArrayComputeStatisticsEx().
    9869             :  *
    9870             :  * @param bApproxOK Currently ignored. In the future, should be set to true
    9871             :  * if statistics on the whole array are wished, or to false if a subset of it
    9872             :  * may be used.
    9873             :  *
    9874             :  * @param pdfMin Location into which to load image minimum (may be NULL).
    9875             :  *
    9876             :  * @param pdfMax Location into which to load image maximum (may be NULL).-
    9877             :  *
    9878             :  * @param pdfMean Location into which to load image mean (may be NULL).
    9879             :  *
    9880             :  * @param pdfStdDev Location into which to load image standard deviation
    9881             :  * (may be NULL).
    9882             :  *
    9883             :  * @param pnValidCount Number of samples whose value is different from the
    9884             :  * nodata value. (may be NULL)
    9885             :  *
    9886             :  * @param pfnProgress a function to call to report progress, or NULL.
    9887             :  *
    9888             :  * @param pProgressData application data to pass to the progress function.
    9889             :  *
    9890             :  * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
    9891             :  *                     Options are driver specific. For now the netCDF and Zarr
    9892             :  *                     drivers recognize UPDATE_METADATA=YES, whose effect is
    9893             :  *                     to add or update the actual_range attribute with the
    9894             :  *                     computed min/max, only if done on the full array, in non
    9895             :  *                     approximate mode, and the dataset is opened in update
    9896             :  *                     mode.
    9897             :  *
    9898             :  * @return true on success
    9899             :  *
    9900             :  * @since GDAL 3.2
    9901             :  */
    9902             : 
    9903          10 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
    9904             :                                     double *pdfMax, double *pdfMean,
    9905             :                                     double *pdfStdDev, GUInt64 *pnValidCount,
    9906             :                                     GDALProgressFunc pfnProgress,
    9907             :                                     void *pProgressData,
    9908             :                                     CSLConstList papszOptions)
    9909             : {
    9910             :     struct StatsPerChunkType
    9911             :     {
    9912             :         const GDALMDArray *array = nullptr;
    9913             :         std::shared_ptr<GDALMDArray> poMask{};
    9914             :         double dfMin = std::numeric_limits<double>::max();
    9915             :         double dfMax = -std::numeric_limits<double>::max();
    9916             :         double dfMean = 0.0;
    9917             :         double dfM2 = 0.0;
    9918             :         GUInt64 nValidCount = 0;
    9919             :         std::vector<GByte> abyData{};
    9920             :         std::vector<double> adfData{};
    9921             :         std::vector<GByte> abyMaskData{};
    9922             :         GDALProgressFunc pfnProgress = nullptr;
    9923             :         void *pProgressData = nullptr;
    9924             :     };
    9925             : 
    9926          10 :     const auto PerChunkFunc = [](GDALAbstractMDArray *,
    9927             :                                  const GUInt64 *chunkArrayStartIdx,
    9928             :                                  const size_t *chunkCount, GUInt64 iCurChunk,
    9929             :                                  GUInt64 nChunkCount, void *pUserData)
    9930             :     {
    9931          10 :         StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
    9932          10 :         const GDALMDArray *array = data->array;
    9933          10 :         const GDALMDArray *poMask = data->poMask.get();
    9934          10 :         const size_t nDims = array->GetDimensionCount();
    9935          10 :         size_t nVals = 1;
    9936          27 :         for (size_t i = 0; i < nDims; i++)
    9937          17 :             nVals *= chunkCount[i];
    9938             : 
    9939             :         // Get mask
    9940          10 :         data->abyMaskData.resize(nVals);
    9941          10 :         if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
    9942          10 :                            poMask->GetDataType(), &data->abyMaskData[0])))
    9943             :         {
    9944           0 :             return false;
    9945             :         }
    9946             : 
    9947             :         // Get data
    9948          10 :         const auto &oType = array->GetDataType();
    9949          10 :         if (oType.GetNumericDataType() == GDT_Float64)
    9950             :         {
    9951           4 :             data->adfData.resize(nVals);
    9952           4 :             if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
    9953           4 :                              oType, &data->adfData[0]))
    9954             :             {
    9955           0 :                 return false;
    9956             :             }
    9957             :         }
    9958             :         else
    9959             :         {
    9960           6 :             data->abyData.resize(nVals * oType.GetSize());
    9961           6 :             if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
    9962           6 :                              oType, &data->abyData[0]))
    9963             :             {
    9964           0 :                 return false;
    9965             :             }
    9966           6 :             data->adfData.resize(nVals);
    9967           6 :             GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
    9968           6 :                             static_cast<int>(oType.GetSize()),
    9969           6 :                             &data->adfData[0], GDT_Float64,
    9970             :                             static_cast<int>(sizeof(double)),
    9971             :                             static_cast<GPtrDiff_t>(nVals));
    9972             :         }
    9973         469 :         for (size_t i = 0; i < nVals; i++)
    9974             :         {
    9975         459 :             if (data->abyMaskData[i])
    9976             :             {
    9977         454 :                 const double dfValue = data->adfData[i];
    9978         454 :                 data->dfMin = std::min(data->dfMin, dfValue);
    9979         454 :                 data->dfMax = std::max(data->dfMax, dfValue);
    9980         454 :                 data->nValidCount++;
    9981         454 :                 const double dfDelta = dfValue - data->dfMean;
    9982         454 :                 data->dfMean += dfDelta / data->nValidCount;
    9983         454 :                 data->dfM2 += dfDelta * (dfValue - data->dfMean);
    9984             :             }
    9985             :         }
    9986          10 :         if (data->pfnProgress &&
    9987           0 :             !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
    9988             :                                "", data->pProgressData))
    9989             :         {
    9990           0 :             return false;
    9991             :         }
    9992          10 :         return true;
    9993             :     };
    9994             : 
    9995          10 :     const auto &oType = GetDataType();
    9996          20 :     if (oType.GetClass() != GEDTC_NUMERIC ||
    9997          10 :         GDALDataTypeIsComplex(oType.GetNumericDataType()))
    9998             :     {
    9999           0 :         CPLError(
   10000             :             CE_Failure, CPLE_NotSupported,
   10001             :             "Statistics can only be computed on non-complex numeric data type");
   10002           0 :         return false;
   10003             :     }
   10004             : 
   10005          10 :     const size_t nDims = GetDimensionCount();
   10006          20 :     std::vector<GUInt64> arrayStartIdx(nDims);
   10007          20 :     std::vector<GUInt64> count(nDims);
   10008          10 :     const auto &poDims = GetDimensions();
   10009          27 :     for (size_t i = 0; i < nDims; i++)
   10010             :     {
   10011          17 :         count[i] = poDims[i]->GetSize();
   10012             :     }
   10013          10 :     const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
   10014             :     const size_t nMaxChunkSize =
   10015             :         pszSwathSize
   10016          10 :             ? static_cast<size_t>(
   10017           0 :                   std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
   10018           0 :                            CPLAtoGIntBig(pszSwathSize)))
   10019             :             : static_cast<size_t>(
   10020          10 :                   std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
   10021          10 :                            GDALGetCacheMax64() / 4));
   10022          20 :     StatsPerChunkType sData;
   10023          10 :     sData.array = this;
   10024          10 :     sData.poMask = GetMask(nullptr);
   10025          10 :     if (sData.poMask == nullptr)
   10026             :     {
   10027           0 :         return false;
   10028             :     }
   10029          10 :     sData.pfnProgress = pfnProgress;
   10030          10 :     sData.pProgressData = pProgressData;
   10031          10 :     if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
   10032          20 :                          GetProcessingChunkSize(nMaxChunkSize).data(),
   10033          10 :                          PerChunkFunc, &sData))
   10034             :     {
   10035           0 :         return false;
   10036             :     }
   10037             : 
   10038          10 :     if (pdfMin)
   10039          10 :         *pdfMin = sData.dfMin;
   10040             : 
   10041          10 :     if (pdfMax)
   10042          10 :         *pdfMax = sData.dfMax;
   10043             : 
   10044          10 :     if (pdfMean)
   10045           8 :         *pdfMean = sData.dfMean;
   10046             : 
   10047             :     const double dfStdDev =
   10048          10 :         sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
   10049          10 :     if (pdfStdDev)
   10050           8 :         *pdfStdDev = dfStdDev;
   10051             : 
   10052          10 :     if (pnValidCount)
   10053           8 :         *pnValidCount = sData.nValidCount;
   10054             : 
   10055          10 :     SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
   10056          10 :                   sData.nValidCount, papszOptions);
   10057             : 
   10058          10 :     return true;
   10059             : }
   10060             : 
   10061             : /************************************************************************/
   10062             : /*                            SetStatistics()                           */
   10063             : /************************************************************************/
   10064             : //! @cond Doxygen_Suppress
   10065           5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
   10066             :                                 double /* dfMax */, double /* dfMean */,
   10067             :                                 double /* dfStdDev */,
   10068             :                                 GUInt64 /* nValidCount */,
   10069             :                                 CSLConstList /* papszOptions */)
   10070             : {
   10071           5 :     CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
   10072           5 :     return false;
   10073             : }
   10074             : 
   10075             : //! @endcond
   10076             : 
   10077             : /************************************************************************/
   10078             : /*                           ClearStatistics()                          */
   10079             : /************************************************************************/
   10080             : 
   10081             : /**
   10082             :  * \brief Clear statistics.
   10083             :  *
   10084             :  * @since GDAL 3.4
   10085             :  */
   10086           0 : void GDALMDArray::ClearStatistics()
   10087             : {
   10088           0 : }
   10089             : 
   10090             : /************************************************************************/
   10091             : /*                      GetCoordinateVariables()                        */
   10092             : /************************************************************************/
   10093             : 
   10094             : /**
   10095             :  * \brief Return coordinate variables.
   10096             :  *
   10097             :  * Coordinate variables are an alternate way of indexing an array that can
   10098             :  * be sometimes used. For example, an array collected through remote sensing
   10099             :  * might be indexed by (scanline, pixel). But there can be
   10100             :  * a longitude and latitude arrays alongside that are also both indexed by
   10101             :  * (scanline, pixel), and are referenced from operational arrays for
   10102             :  * reprojection purposes.
   10103             :  *
   10104             :  * For netCDF, this will return the arrays referenced by the "coordinates"
   10105             :  * attribute.
   10106             :  *
   10107             :  * This method is the same as the C function
   10108             :  * GDALMDArrayGetCoordinateVariables().
   10109             :  *
   10110             :  * @return a vector of arrays
   10111             :  *
   10112             :  * @since GDAL 3.4
   10113             :  */
   10114             : 
   10115             : std::vector<std::shared_ptr<GDALMDArray>>
   10116          13 : GDALMDArray::GetCoordinateVariables() const
   10117             : {
   10118          13 :     return {};
   10119             : }
   10120             : 
   10121             : /************************************************************************/
   10122             : /*                       ~GDALExtendedDataType()                        */
   10123             : /************************************************************************/
   10124             : 
   10125             : GDALExtendedDataType::~GDALExtendedDataType() = default;
   10126             : 
   10127             : /************************************************************************/
   10128             : /*                        GDALExtendedDataType()                        */
   10129             : /************************************************************************/
   10130             : 
   10131        8308 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
   10132        8308 :                                            GDALExtendedDataTypeSubType eSubType)
   10133             :     : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
   10134        8308 :       m_nMaxStringLength(nMaxStringLength)
   10135             : {
   10136        8308 : }
   10137             : 
   10138             : /************************************************************************/
   10139             : /*                        GDALExtendedDataType()                        */
   10140             : /************************************************************************/
   10141             : 
   10142       35323 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
   10143             :     : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
   10144       35323 :       m_nSize(GDALGetDataTypeSizeBytes(eType))
   10145             : {
   10146       35323 : }
   10147             : 
   10148             : /************************************************************************/
   10149             : /*                        GDALExtendedDataType()                        */
   10150             : /************************************************************************/
   10151             : 
   10152         632 : GDALExtendedDataType::GDALExtendedDataType(
   10153             :     const std::string &osName, size_t nTotalSize,
   10154         632 :     std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
   10155             :     : m_osName(osName), m_eClass(GEDTC_COMPOUND),
   10156         632 :       m_aoComponents(std::move(components)), m_nSize(nTotalSize)
   10157             : {
   10158         632 : }
   10159             : 
   10160             : /************************************************************************/
   10161             : /*                        GDALExtendedDataType()                        */
   10162             : /************************************************************************/
   10163             : 
   10164             : /** Copy constructor. */
   10165       15444 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
   10166       30888 :     : m_osName(other.m_osName), m_eClass(other.m_eClass),
   10167       15444 :       m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
   10168       15444 :       m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength)
   10169             : {
   10170       15444 :     if (m_eClass == GEDTC_COMPOUND)
   10171             :     {
   10172         431 :         for (const auto &elt : other.m_aoComponents)
   10173             :         {
   10174         281 :             m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
   10175             :         }
   10176             :     }
   10177       15444 : }
   10178             : 
   10179             : /************************************************************************/
   10180             : /*                            operator= ()                              */
   10181             : /************************************************************************/
   10182             : 
   10183             : /** Copy assignment. */
   10184             : GDALExtendedDataType &
   10185         606 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
   10186             : {
   10187         606 :     if (this != &other)
   10188             :     {
   10189         606 :         m_osName = other.m_osName;
   10190         606 :         m_eClass = other.m_eClass;
   10191         606 :         m_eSubType = other.m_eSubType;
   10192         606 :         m_eNumericDT = other.m_eNumericDT;
   10193         606 :         m_nSize = other.m_nSize;
   10194         606 :         m_nMaxStringLength = other.m_nMaxStringLength;
   10195         606 :         m_aoComponents.clear();
   10196         606 :         if (m_eClass == GEDTC_COMPOUND)
   10197             :         {
   10198           0 :             for (const auto &elt : other.m_aoComponents)
   10199             :             {
   10200           0 :                 m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
   10201             :             }
   10202             :         }
   10203             :     }
   10204         606 :     return *this;
   10205             : }
   10206             : 
   10207             : /************************************************************************/
   10208             : /*                            operator= ()                              */
   10209             : /************************************************************************/
   10210             : 
   10211             : /** Move assignment. */
   10212             : GDALExtendedDataType &
   10213       14546 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other)
   10214             : {
   10215       14546 :     m_osName = std::move(other.m_osName);
   10216       14546 :     m_eClass = other.m_eClass;
   10217       14546 :     m_eSubType = other.m_eSubType;
   10218       14546 :     m_eNumericDT = other.m_eNumericDT;
   10219       14546 :     m_nSize = other.m_nSize;
   10220       14546 :     m_nMaxStringLength = other.m_nMaxStringLength;
   10221       14546 :     m_aoComponents = std::move(other.m_aoComponents);
   10222       14546 :     other.m_eClass = GEDTC_NUMERIC;
   10223       14546 :     other.m_eNumericDT = GDT_Unknown;
   10224       14546 :     other.m_nSize = 0;
   10225       14546 :     other.m_nMaxStringLength = 0;
   10226       14546 :     return *this;
   10227             : }
   10228             : 
   10229             : /************************************************************************/
   10230             : /*                           Create()                                   */
   10231             : /************************************************************************/
   10232             : 
   10233             : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
   10234             :  *
   10235             :  * This is the same as the C function GDALExtendedDataTypeCreate()
   10236             :  *
   10237             :  * @param eType Numeric data type. Must be different from GDT_Unknown and
   10238             :  * GDT_TypeCount
   10239             :  */
   10240       35316 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
   10241             : {
   10242       35316 :     return GDALExtendedDataType(eType);
   10243             : }
   10244             : 
   10245             : /************************************************************************/
   10246             : /*                           Create()                                   */
   10247             : /************************************************************************/
   10248             : 
   10249             : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
   10250             :  *
   10251             :  * This is the same as the C function GDALExtendedDataTypeCreateCompound()
   10252             :  *
   10253             :  * @param osName Type name.
   10254             :  * @param nTotalSize Total size of the type in bytes.
   10255             :  *                   Should be large enough to store all components.
   10256             :  * @param components Components of the compound type.
   10257             :  */
   10258         639 : GDALExtendedDataType GDALExtendedDataType::Create(
   10259             :     const std::string &osName, size_t nTotalSize,
   10260             :     std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
   10261             : {
   10262         639 :     size_t nLastOffset = 0;
   10263             :     // Some arbitrary threshold to avoid potential integer overflows
   10264         639 :     if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
   10265             :     {
   10266           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10267           2 :         return GDALExtendedDataType(GDT_Unknown);
   10268             :     }
   10269        3155 :     for (const auto &comp : components)
   10270             :     {
   10271             :         // Check alignment too ?
   10272        2519 :         if (comp->GetOffset() < nLastOffset)
   10273             :         {
   10274           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10275           1 :             return GDALExtendedDataType(GDT_Unknown);
   10276             :         }
   10277        2518 :         nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
   10278             :     }
   10279         636 :     if (nTotalSize < nLastOffset)
   10280             :     {
   10281           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
   10282           1 :         return GDALExtendedDataType(GDT_Unknown);
   10283             :     }
   10284         635 :     if (nTotalSize == 0 || components.empty())
   10285             :     {
   10286           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
   10287           3 :         return GDALExtendedDataType(GDT_Unknown);
   10288             :     }
   10289         632 :     return GDALExtendedDataType(osName, nTotalSize, std::move(components));
   10290             : }
   10291             : 
   10292             : /************************************************************************/
   10293             : /*                           Create()                                   */
   10294             : /************************************************************************/
   10295             : 
   10296             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   10297             :  *
   10298             :  * This is the same as the C function GDALExtendedDataTypeCreateString().
   10299             :  *
   10300             :  * @param nMaxStringLength maximum length of a string in bytes. 0 if
   10301             :  * unknown/unlimited
   10302             :  * @param eSubType Subtype.
   10303             :  */
   10304             : GDALExtendedDataType
   10305        8308 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
   10306             :                                    GDALExtendedDataTypeSubType eSubType)
   10307             : {
   10308        8308 :     return GDALExtendedDataType(nMaxStringLength, eSubType);
   10309             : }
   10310             : 
   10311             : /************************************************************************/
   10312             : /*                           operator==()                               */
   10313             : /************************************************************************/
   10314             : 
   10315             : /** Equality operator.
   10316             :  *
   10317             :  * This is the same as the C function GDALExtendedDataTypeEquals().
   10318             :  */
   10319        2240 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
   10320             : {
   10321        2213 :     if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
   10322        4453 :         m_nSize != other.m_nSize || m_osName != other.m_osName)
   10323             :     {
   10324         177 :         return false;
   10325             :     }
   10326        2063 :     if (m_eClass == GEDTC_NUMERIC)
   10327             :     {
   10328         866 :         return m_eNumericDT == other.m_eNumericDT;
   10329             :     }
   10330        1197 :     if (m_eClass == GEDTC_STRING)
   10331             :     {
   10332        1016 :         return true;
   10333             :     }
   10334         181 :     CPLAssert(m_eClass == GEDTC_COMPOUND);
   10335         181 :     if (m_aoComponents.size() != other.m_aoComponents.size())
   10336             :     {
   10337           2 :         return false;
   10338             :     }
   10339         806 :     for (size_t i = 0; i < m_aoComponents.size(); i++)
   10340             :     {
   10341         627 :         if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
   10342             :         {
   10343           0 :             return false;
   10344             :         }
   10345             :     }
   10346         179 :     return true;
   10347             : }
   10348             : 
   10349             : /************************************************************************/
   10350             : /*                        CanConvertTo()                                */
   10351             : /************************************************************************/
   10352             : 
   10353             : /** Return whether this data type can be converted to the other one.
   10354             :  *
   10355             :  * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
   10356             :  *
   10357             :  * @param other Target data type for the conversion being considered.
   10358             :  */
   10359        8133 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
   10360             : {
   10361        8133 :     if (m_eClass == GEDTC_NUMERIC)
   10362             :     {
   10363        5781 :         if (m_eNumericDT == GDT_Unknown)
   10364           0 :             return false;
   10365        5781 :         if (other.m_eClass == GEDTC_NUMERIC &&
   10366        5694 :             other.m_eNumericDT == GDT_Unknown)
   10367           0 :             return false;
   10368        5868 :         return other.m_eClass == GEDTC_NUMERIC ||
   10369        5868 :                other.m_eClass == GEDTC_STRING;
   10370             :     }
   10371        2352 :     if (m_eClass == GEDTC_STRING)
   10372             :     {
   10373        2213 :         return other.m_eClass == m_eClass;
   10374             :     }
   10375         139 :     CPLAssert(m_eClass == GEDTC_COMPOUND);
   10376         139 :     if (other.m_eClass != GEDTC_COMPOUND)
   10377           0 :         return false;
   10378             :     std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
   10379         278 :         srcComponents;
   10380         568 :     for (const auto &srcComp : m_aoComponents)
   10381             :     {
   10382         429 :         srcComponents[srcComp->GetName()] = &srcComp;
   10383             :     }
   10384         419 :     for (const auto &dstComp : other.m_aoComponents)
   10385             :     {
   10386         281 :         auto oIter = srcComponents.find(dstComp->GetName());
   10387         281 :         if (oIter == srcComponents.end())
   10388           1 :             return false;
   10389         280 :         if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
   10390           0 :             return false;
   10391             :     }
   10392         138 :     return true;
   10393             : }
   10394             : 
   10395             : /************************************************************************/
   10396             : /*                     NeedsFreeDynamicMemory()                         */
   10397             : /************************************************************************/
   10398             : 
   10399             : /** Return whether the data type holds dynamically allocated memory, that
   10400             :  * needs to be freed with FreeDynamicMemory().
   10401             :  *
   10402             :  */
   10403        3528 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
   10404             : {
   10405        3528 :     switch (m_eClass)
   10406             :     {
   10407         844 :         case GEDTC_STRING:
   10408         844 :             return true;
   10409             : 
   10410        2611 :         case GEDTC_NUMERIC:
   10411        2611 :             return false;
   10412             : 
   10413          73 :         case GEDTC_COMPOUND:
   10414             :         {
   10415         186 :             for (const auto &comp : m_aoComponents)
   10416             :             {
   10417         172 :                 if (comp->GetType().NeedsFreeDynamicMemory())
   10418          59 :                     return true;
   10419             :             }
   10420             :         }
   10421             :     }
   10422          14 :     return false;
   10423             : }
   10424             : 
   10425             : /************************************************************************/
   10426             : /*                        FreeDynamicMemory()                           */
   10427             : /************************************************************************/
   10428             : 
   10429             : /** Release the dynamic memory (strings typically) from a raw value.
   10430             :  *
   10431             :  * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
   10432             :  *
   10433             :  * @param pBuffer Raw buffer of a single element of an attribute or array value.
   10434             :  */
   10435        3260 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
   10436             : {
   10437        3260 :     switch (m_eClass)
   10438             :     {
   10439        2286 :         case GEDTC_STRING:
   10440             :         {
   10441             :             char *pszStr;
   10442        2286 :             memcpy(&pszStr, pBuffer, sizeof(char *));
   10443        2286 :             if (pszStr)
   10444             :             {
   10445        1781 :                 VSIFree(pszStr);
   10446             :             }
   10447        2286 :             break;
   10448             :         }
   10449             : 
   10450         835 :         case GEDTC_NUMERIC:
   10451             :         {
   10452         835 :             break;
   10453             :         }
   10454             : 
   10455         139 :         case GEDTC_COMPOUND:
   10456             :         {
   10457         139 :             GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
   10458         605 :             for (const auto &comp : m_aoComponents)
   10459             :             {
   10460         932 :                 comp->GetType().FreeDynamicMemory(pabyBuffer +
   10461         466 :                                                   comp->GetOffset());
   10462             :             }
   10463         139 :             break;
   10464             :         }
   10465             :     }
   10466        3260 : }
   10467             : 
   10468             : /************************************************************************/
   10469             : /*                      ~GDALEDTComponent()                             */
   10470             : /************************************************************************/
   10471             : 
   10472             : GDALEDTComponent::~GDALEDTComponent() = default;
   10473             : 
   10474             : /************************************************************************/
   10475             : /*                      GDALEDTComponent()                              */
   10476             : /************************************************************************/
   10477             : 
   10478             : /** constructor of a GDALEDTComponent
   10479             :  *
   10480             :  * This is the same as the C function GDALEDTComponendCreate()
   10481             :  *
   10482             :  * @param name Component name
   10483             :  * @param offset Offset in byte of the component in the compound data type.
   10484             :  *               In case of nesting of compound data type, this should be
   10485             :  *               the offset to the immediate belonging data type, not to the
   10486             :  *               higher level one.
   10487             :  * @param type   Component data type.
   10488             :  */
   10489        2510 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
   10490        2510 :                                    const GDALExtendedDataType &type)
   10491        2510 :     : m_osName(name), m_nOffset(offset), m_oType(type)
   10492             : {
   10493        2510 : }
   10494             : 
   10495             : /************************************************************************/
   10496             : /*                      GDALEDTComponent()                              */
   10497             : /************************************************************************/
   10498             : 
   10499             : /** Copy constructor. */
   10500             : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
   10501             : 
   10502             : /************************************************************************/
   10503             : /*                           operator==()                               */
   10504             : /************************************************************************/
   10505             : 
   10506             : /** Equality operator.
   10507             :  */
   10508         627 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
   10509             : {
   10510        1254 :     return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
   10511        1254 :            m_oType == other.m_oType;
   10512             : }
   10513             : 
   10514             : /************************************************************************/
   10515             : /*                        ~GDALDimension()                              */
   10516             : /************************************************************************/
   10517             : 
   10518             : GDALDimension::~GDALDimension() = default;
   10519             : 
   10520             : /************************************************************************/
   10521             : /*                         GDALDimension()                              */
   10522             : /************************************************************************/
   10523             : 
   10524             : //! @cond Doxygen_Suppress
   10525             : /** Constructor.
   10526             :  *
   10527             :  * @param osParentName Parent name
   10528             :  * @param osName name
   10529             :  * @param osType type. See GetType().
   10530             :  * @param osDirection direction. See GetDirection().
   10531             :  * @param nSize size.
   10532             :  */
   10533        8119 : GDALDimension::GDALDimension(const std::string &osParentName,
   10534             :                              const std::string &osName,
   10535             :                              const std::string &osType,
   10536        8119 :                              const std::string &osDirection, GUInt64 nSize)
   10537             :     : m_osName(osName),
   10538             :       m_osFullName(
   10539        8119 :           !osParentName.empty()
   10540       12004 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
   10541             :               : osName),
   10542       28242 :       m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
   10543             : {
   10544        8119 : }
   10545             : 
   10546             : //! @endcond
   10547             : 
   10548             : /************************************************************************/
   10549             : /*                         GetIndexingVariable()                        */
   10550             : /************************************************************************/
   10551             : 
   10552             : /** Return the variable that is used to index the dimension (if there is one).
   10553             :  *
   10554             :  * This is the array, typically one-dimensional, describing the values taken
   10555             :  * by the dimension.
   10556             :  */
   10557          49 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
   10558             : {
   10559          49 :     return nullptr;
   10560             : }
   10561             : 
   10562             : /************************************************************************/
   10563             : /*                         SetIndexingVariable()                        */
   10564             : /************************************************************************/
   10565             : 
   10566             : /** Set the variable that is used to index the dimension.
   10567             :  *
   10568             :  * This is the array, typically one-dimensional, describing the values taken
   10569             :  * by the dimension.
   10570             :  *
   10571             :  * Optionally implemented by drivers.
   10572             :  *
   10573             :  * Drivers known to implement it: MEM.
   10574             :  *
   10575             :  * @param poArray Variable to use to index the dimension.
   10576             :  * @return true in case of success.
   10577             :  */
   10578           3 : bool GDALDimension::SetIndexingVariable(
   10579             :     CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
   10580             : {
   10581           3 :     CPLError(CE_Failure, CPLE_NotSupported,
   10582             :              "SetIndexingVariable() not implemented");
   10583           3 :     return false;
   10584             : }
   10585             : 
   10586             : /************************************************************************/
   10587             : /*                            Rename()                                  */
   10588             : /************************************************************************/
   10589             : 
   10590             : /** Rename the dimension.
   10591             :  *
   10592             :  * This is not implemented by all drivers.
   10593             :  *
   10594             :  * Drivers known to implement it: MEM, netCDF, ZARR.
   10595             :  *
   10596             :  * This is the same as the C function GDALDimensionRename().
   10597             :  *
   10598             :  * @param osNewName New name.
   10599             :  *
   10600             :  * @return true in case of success
   10601             :  * @since GDAL 3.8
   10602             :  */
   10603           0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
   10604             : {
   10605           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
   10606           0 :     return false;
   10607             : }
   10608             : 
   10609             : /************************************************************************/
   10610             : /*                         BaseRename()                                 */
   10611             : /************************************************************************/
   10612             : 
   10613             : //! @cond Doxygen_Suppress
   10614           8 : void GDALDimension::BaseRename(const std::string &osNewName)
   10615             : {
   10616           8 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
   10617           8 :     m_osFullName += osNewName;
   10618           8 :     m_osName = osNewName;
   10619           8 : }
   10620             : 
   10621             : //! @endcond
   10622             : 
   10623             : //! @cond Doxygen_Suppress
   10624             : /************************************************************************/
   10625             : /*                          ParentRenamed()                             */
   10626             : /************************************************************************/
   10627             : 
   10628           8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
   10629             : {
   10630           8 :     m_osFullName = osNewParentFullName;
   10631           8 :     m_osFullName += "/";
   10632           8 :     m_osFullName += m_osName;
   10633           8 : }
   10634             : 
   10635             : //! @endcond
   10636             : 
   10637             : //! @cond Doxygen_Suppress
   10638             : /************************************************************************/
   10639             : /*                          ParentDeleted()                             */
   10640             : /************************************************************************/
   10641             : 
   10642           4 : void GDALDimension::ParentDeleted()
   10643             : {
   10644           4 : }
   10645             : 
   10646             : //! @endcond
   10647             : 
   10648             : /************************************************************************/
   10649             : /************************************************************************/
   10650             : /************************************************************************/
   10651             : /*                              C API                                   */
   10652             : /************************************************************************/
   10653             : /************************************************************************/
   10654             : /************************************************************************/
   10655             : 
   10656             : /************************************************************************/
   10657             : /*                      GDALExtendedDataTypeCreate()                    */
   10658             : /************************************************************************/
   10659             : 
   10660             : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
   10661             :  *
   10662             :  * This is the same as the C++ method GDALExtendedDataType::Create()
   10663             :  *
   10664             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10665             :  *
   10666             :  * @param eType Numeric data type. Must be different from GDT_Unknown and
   10667             :  * GDT_TypeCount
   10668             :  *
   10669             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10670             :  */
   10671        1984 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
   10672             : {
   10673        1984 :     if (CPL_UNLIKELY(eType == GDT_Unknown || eType == GDT_TypeCount))
   10674             :     {
   10675           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
   10676             :                  "Illegal GDT_Unknown/GDT_TypeCount argument");
   10677           0 :         return nullptr;
   10678             :     }
   10679             :     return new GDALExtendedDataTypeHS(
   10680        1984 :         new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
   10681             : }
   10682             : 
   10683             : /************************************************************************/
   10684             : /*                    GDALExtendedDataTypeCreateString()                */
   10685             : /************************************************************************/
   10686             : 
   10687             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   10688             :  *
   10689             :  * This is the same as the C++ method GDALExtendedDataType::CreateString()
   10690             :  *
   10691             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10692             :  *
   10693             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10694             :  */
   10695           0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
   10696             : {
   10697           0 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   10698           0 :         GDALExtendedDataType::CreateString(nMaxStringLength)));
   10699             : }
   10700             : 
   10701             : /************************************************************************/
   10702             : /*                   GDALExtendedDataTypeCreateStringEx()               */
   10703             : /************************************************************************/
   10704             : 
   10705             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   10706             :  *
   10707             :  * This is the same as the C++ method GDALExtendedDataType::CreateString()
   10708             :  *
   10709             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10710             :  *
   10711             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10712             :  * @since GDAL 3.4
   10713             :  */
   10714             : GDALExtendedDataTypeH
   10715         188 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
   10716             :                                    GDALExtendedDataTypeSubType eSubType)
   10717             : {
   10718         188 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   10719         188 :         GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
   10720             : }
   10721             : 
   10722             : /************************************************************************/
   10723             : /*                   GDALExtendedDataTypeCreateCompound()               */
   10724             : /************************************************************************/
   10725             : 
   10726             : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
   10727             :  *
   10728             :  * This is the same as the C++ method GDALExtendedDataType::Create(const
   10729             :  * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
   10730             :  *
   10731             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10732             :  *
   10733             :  * @param pszName Type name.
   10734             :  * @param nTotalSize Total size of the type in bytes.
   10735             :  *                   Should be large enough to store all components.
   10736             :  * @param nComponents Number of components in comps array.
   10737             :  * @param comps Components.
   10738             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10739             :  */
   10740             : GDALExtendedDataTypeH
   10741          22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
   10742             :                                    size_t nComponents,
   10743             :                                    const GDALEDTComponentH *comps)
   10744             : {
   10745          44 :     std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
   10746          54 :     for (size_t i = 0; i < nComponents; i++)
   10747             :     {
   10748          64 :         compsCpp.emplace_back(std::unique_ptr<GDALEDTComponent>(
   10749          64 :             new GDALEDTComponent(*(comps[i]->m_poImpl.get()))));
   10750             :     }
   10751             :     auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
   10752          66 :                                            std::move(compsCpp));
   10753          22 :     if (dt.GetClass() != GEDTC_COMPOUND)
   10754           6 :         return nullptr;
   10755          16 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(dt));
   10756             : }
   10757             : 
   10758             : /************************************************************************/
   10759             : /*                     GDALExtendedDataTypeRelease()                    */
   10760             : /************************************************************************/
   10761             : 
   10762             : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
   10763             :  *
   10764             :  * Note: when applied on a object coming from a driver, this does not
   10765             :  * destroy the object in the file, database, etc...
   10766             :  */
   10767        6503 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
   10768             : {
   10769        6503 :     delete hEDT;
   10770        6503 : }
   10771             : 
   10772             : /************************************************************************/
   10773             : /*                     GDALExtendedDataTypeGetName()                    */
   10774             : /************************************************************************/
   10775             : 
   10776             : /** Return type name.
   10777             :  *
   10778             :  * This is the same as the C++ method GDALExtendedDataType::GetName()
   10779             :  */
   10780           7 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
   10781             : {
   10782           7 :     VALIDATE_POINTER1(hEDT, __func__, "");
   10783           7 :     return hEDT->m_poImpl->GetName().c_str();
   10784             : }
   10785             : 
   10786             : /************************************************************************/
   10787             : /*                     GDALExtendedDataTypeGetClass()                    */
   10788             : /************************************************************************/
   10789             : 
   10790             : /** Return type class.
   10791             :  *
   10792             :  * This is the same as the C++ method GDALExtendedDataType::GetClass()
   10793             :  */
   10794             : GDALExtendedDataTypeClass
   10795        7500 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
   10796             : {
   10797        7500 :     VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
   10798        7500 :     return hEDT->m_poImpl->GetClass();
   10799             : }
   10800             : 
   10801             : /************************************************************************/
   10802             : /*               GDALExtendedDataTypeGetNumericDataType()               */
   10803             : /************************************************************************/
   10804             : 
   10805             : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
   10806             :  *
   10807             :  * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
   10808             :  */
   10809         584 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
   10810             : {
   10811         584 :     VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
   10812         584 :     return hEDT->m_poImpl->GetNumericDataType();
   10813             : }
   10814             : 
   10815             : /************************************************************************/
   10816             : /*                   GDALExtendedDataTypeGetSize()                      */
   10817             : /************************************************************************/
   10818             : 
   10819             : /** Return data type size in bytes.
   10820             :  *
   10821             :  * This is the same as the C++ method GDALExtendedDataType::GetSize()
   10822             :  */
   10823        2494 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
   10824             : {
   10825        2494 :     VALIDATE_POINTER1(hEDT, __func__, 0);
   10826        2494 :     return hEDT->m_poImpl->GetSize();
   10827             : }
   10828             : 
   10829             : /************************************************************************/
   10830             : /*              GDALExtendedDataTypeGetMaxStringLength()                */
   10831             : /************************************************************************/
   10832             : 
   10833             : /** Return the maximum length of a string in bytes.
   10834             :  *
   10835             :  * 0 indicates unknown/unlimited string.
   10836             :  *
   10837             :  * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
   10838             :  */
   10839           3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
   10840             : {
   10841           3 :     VALIDATE_POINTER1(hEDT, __func__, 0);
   10842           3 :     return hEDT->m_poImpl->GetMaxStringLength();
   10843             : }
   10844             : 
   10845             : /************************************************************************/
   10846             : /*                    GDALExtendedDataTypeCanConvertTo()                */
   10847             : /************************************************************************/
   10848             : 
   10849             : /** Return whether this data type can be converted to the other one.
   10850             :  *
   10851             :  * This is the same as the C function GDALExtendedDataType::CanConvertTo()
   10852             :  *
   10853             :  * @param hSourceEDT Source data type for the conversion being considered.
   10854             :  * @param hTargetEDT Target data type for the conversion being considered.
   10855             :  * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
   10856             :  */
   10857           7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
   10858             :                                      GDALExtendedDataTypeH hTargetEDT)
   10859             : {
   10860           7 :     VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
   10861           7 :     VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
   10862           7 :     return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
   10863             : }
   10864             : 
   10865             : /************************************************************************/
   10866             : /*                        GDALExtendedDataTypeEquals()                  */
   10867             : /************************************************************************/
   10868             : 
   10869             : /** Return whether this data type is equal to another one.
   10870             :  *
   10871             :  * This is the same as the C++ method GDALExtendedDataType::operator==()
   10872             :  *
   10873             :  * @param hFirstEDT First data type.
   10874             :  * @param hSecondEDT Second data type.
   10875             :  * @return TRUE if they are equal. FALSE otherwise.
   10876             :  */
   10877          98 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
   10878             :                                GDALExtendedDataTypeH hSecondEDT)
   10879             : {
   10880          98 :     VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
   10881          98 :     VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
   10882          98 :     return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
   10883             : }
   10884             : 
   10885             : /************************************************************************/
   10886             : /*                    GDALExtendedDataTypeGetSubType()                  */
   10887             : /************************************************************************/
   10888             : 
   10889             : /** Return the subtype of a type.
   10890             :  *
   10891             :  * This is the same as the C++ method GDALExtendedDataType::GetSubType()
   10892             :  *
   10893             :  * @param hEDT Data type.
   10894             :  * @return subtype.
   10895             :  * @since 3.4
   10896             :  */
   10897             : GDALExtendedDataTypeSubType
   10898         104 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
   10899             : {
   10900         104 :     VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
   10901         104 :     return hEDT->m_poImpl->GetSubType();
   10902             : }
   10903             : 
   10904             : /************************************************************************/
   10905             : /*                     GDALExtendedDataTypeGetComponents()              */
   10906             : /************************************************************************/
   10907             : 
   10908             : /** Return the components of the data type (only valid when GetClass() ==
   10909             :  * GEDTC_COMPOUND)
   10910             :  *
   10911             :  * The returned array and its content must be freed with
   10912             :  * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
   10913             :  * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
   10914             :  * individual array members).
   10915             :  *
   10916             :  * This is the same as the C++ method GDALExtendedDataType::GetComponents()
   10917             :  *
   10918             :  * @param hEDT Data type
   10919             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   10920             :  * @return an array of *pnCount components.
   10921             :  */
   10922          44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
   10923             :                                                      size_t *pnCount)
   10924             : {
   10925          44 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   10926          44 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   10927          44 :     const auto &components = hEDT->m_poImpl->GetComponents();
   10928             :     auto ret = static_cast<GDALEDTComponentH *>(
   10929          44 :         CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
   10930         131 :     for (size_t i = 0; i < components.size(); i++)
   10931             :     {
   10932          87 :         ret[i] = new GDALEDTComponentHS(*components[i].get());
   10933             :     }
   10934          44 :     *pnCount = components.size();
   10935          44 :     return ret;
   10936             : }
   10937             : 
   10938             : /************************************************************************/
   10939             : /*                     GDALExtendedDataTypeFreeComponents()             */
   10940             : /************************************************************************/
   10941             : 
   10942             : /** Free the return of GDALExtendedDataTypeGetComponents().
   10943             :  *
   10944             :  * @param components return value of GDALExtendedDataTypeGetComponents()
   10945             :  * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
   10946             :  */
   10947          44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
   10948             :                                         size_t nCount)
   10949             : {
   10950         131 :     for (size_t i = 0; i < nCount; i++)
   10951             :     {
   10952          87 :         delete components[i];
   10953             :     }
   10954          44 :     CPLFree(components);
   10955          44 : }
   10956             : 
   10957             : /************************************************************************/
   10958             : /*                         GDALEDTComponentCreate()                     */
   10959             : /************************************************************************/
   10960             : 
   10961             : /** Create a new GDALEDTComponent.
   10962             :  *
   10963             :  * The returned value must be freed with GDALEDTComponentRelease().
   10964             :  *
   10965             :  * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
   10966             :  */
   10967          20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
   10968             :                                          GDALExtendedDataTypeH hType)
   10969             : {
   10970          20 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   10971          20 :     VALIDATE_POINTER1(hType, __func__, nullptr);
   10972             :     return new GDALEDTComponentHS(
   10973          20 :         GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
   10974             : }
   10975             : 
   10976             : /************************************************************************/
   10977             : /*                         GDALEDTComponentRelease()                    */
   10978             : /************************************************************************/
   10979             : 
   10980             : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
   10981             :  *
   10982             :  * Note: when applied on a object coming from a driver, this does not
   10983             :  * destroy the object in the file, database, etc...
   10984             :  */
   10985          61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
   10986             : {
   10987          61 :     delete hComp;
   10988          61 : }
   10989             : 
   10990             : /************************************************************************/
   10991             : /*                         GDALEDTComponentGetName()                    */
   10992             : /************************************************************************/
   10993             : 
   10994             : /** Return the name.
   10995             :  *
   10996             :  * The returned pointer is valid until hComp is released.
   10997             :  *
   10998             :  * This is the same as the C++ method GDALEDTComponent::GetName().
   10999             :  */
   11000          33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
   11001             : {
   11002          33 :     VALIDATE_POINTER1(hComp, __func__, nullptr);
   11003          33 :     return hComp->m_poImpl->GetName().c_str();
   11004             : }
   11005             : 
   11006             : /************************************************************************/
   11007             : /*                       GDALEDTComponentGetOffset()                    */
   11008             : /************************************************************************/
   11009             : 
   11010             : /** Return the offset (in bytes) of the component in the compound data type.
   11011             :  *
   11012             :  * This is the same as the C++ method GDALEDTComponent::GetOffset().
   11013             :  */
   11014          31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
   11015             : {
   11016          31 :     VALIDATE_POINTER1(hComp, __func__, 0);
   11017          31 :     return hComp->m_poImpl->GetOffset();
   11018             : }
   11019             : 
   11020             : /************************************************************************/
   11021             : /*                       GDALEDTComponentGetType()                      */
   11022             : /************************************************************************/
   11023             : 
   11024             : /** Return the data type of the component.
   11025             :  *
   11026             :  * This is the same as the C++ method GDALEDTComponent::GetType().
   11027             :  */
   11028          93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
   11029             : {
   11030          93 :     VALIDATE_POINTER1(hComp, __func__, nullptr);
   11031             :     return new GDALExtendedDataTypeHS(
   11032          93 :         new GDALExtendedDataType(hComp->m_poImpl->GetType()));
   11033             : }
   11034             : 
   11035             : /************************************************************************/
   11036             : /*                           GDALGroupRelease()                         */
   11037             : /************************************************************************/
   11038             : 
   11039             : /** Release the GDAL in-memory object associated with a GDALGroupH.
   11040             :  *
   11041             :  * Note: when applied on a object coming from a driver, this does not
   11042             :  * destroy the object in the file, database, etc...
   11043             :  */
   11044        1385 : void GDALGroupRelease(GDALGroupH hGroup)
   11045             : {
   11046        1385 :     delete hGroup;
   11047        1385 : }
   11048             : 
   11049             : /************************************************************************/
   11050             : /*                           GDALGroupGetName()                         */
   11051             : /************************************************************************/
   11052             : 
   11053             : /** Return the name of the group.
   11054             :  *
   11055             :  * The returned pointer is valid until hGroup is released.
   11056             :  *
   11057             :  * This is the same as the C++ method GDALGroup::GetName().
   11058             :  */
   11059          87 : const char *GDALGroupGetName(GDALGroupH hGroup)
   11060             : {
   11061          87 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11062          87 :     return hGroup->m_poImpl->GetName().c_str();
   11063             : }
   11064             : 
   11065             : /************************************************************************/
   11066             : /*                         GDALGroupGetFullName()                       */
   11067             : /************************************************************************/
   11068             : 
   11069             : /** Return the full name of the group.
   11070             :  *
   11071             :  * The returned pointer is valid until hGroup is released.
   11072             :  *
   11073             :  * This is the same as the C++ method GDALGroup::GetFullName().
   11074             :  */
   11075          41 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
   11076             : {
   11077          41 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11078          41 :     return hGroup->m_poImpl->GetFullName().c_str();
   11079             : }
   11080             : 
   11081             : /************************************************************************/
   11082             : /*                          GDALGroupGetMDArrayNames()                  */
   11083             : /************************************************************************/
   11084             : 
   11085             : /** Return the list of multidimensional array names contained in this group.
   11086             :  *
   11087             :  * This is the same as the C++ method GDALGroup::GetGroupNames().
   11088             :  *
   11089             :  * @return the array names, to be freed with CSLDestroy()
   11090             :  */
   11091         317 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
   11092             : {
   11093         317 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11094         634 :     auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
   11095         634 :     CPLStringList res;
   11096         805 :     for (const auto &name : names)
   11097             :     {
   11098         488 :         res.AddString(name.c_str());
   11099             :     }
   11100         317 :     return res.StealList();
   11101             : }
   11102             : 
   11103             : /************************************************************************/
   11104             : /*                          GDALGroupOpenMDArray()                      */
   11105             : /************************************************************************/
   11106             : 
   11107             : /** Open and return a multidimensional array.
   11108             :  *
   11109             :  * This is the same as the C++ method GDALGroup::OpenMDArray().
   11110             :  *
   11111             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11112             :  */
   11113         761 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
   11114             :                                   CSLConstList papszOptions)
   11115             : {
   11116         761 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11117         761 :     VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
   11118        2283 :     auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
   11119        2283 :                                                papszOptions);
   11120         761 :     if (!array)
   11121          28 :         return nullptr;
   11122         733 :     return new GDALMDArrayHS(array);
   11123             : }
   11124             : 
   11125             : /************************************************************************/
   11126             : /*                  GDALGroupOpenMDArrayFromFullname()                  */
   11127             : /************************************************************************/
   11128             : 
   11129             : /** Open and return a multidimensional array from its fully qualified name.
   11130             :  *
   11131             :  * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
   11132             :  *
   11133             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11134             :  *
   11135             :  * @since GDAL 3.2
   11136             :  */
   11137          16 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
   11138             :                                               const char *pszFullname,
   11139             :                                               CSLConstList papszOptions)
   11140             : {
   11141          16 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11142          16 :     VALIDATE_POINTER1(pszFullname, __func__, nullptr);
   11143          16 :     auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
   11144          48 :         std::string(pszFullname), papszOptions);
   11145          16 :     if (!array)
   11146           2 :         return nullptr;
   11147          14 :     return new GDALMDArrayHS(array);
   11148             : }
   11149             : 
   11150             : /************************************************************************/
   11151             : /*                      GDALGroupResolveMDArray()                       */
   11152             : /************************************************************************/
   11153             : 
   11154             : /** Locate an array in a group and its subgroups by name.
   11155             :  *
   11156             :  * See GDALGroup::ResolveMDArray() for description of the behavior.
   11157             :  * @since GDAL 3.2
   11158             :  */
   11159          19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
   11160             :                                      const char *pszStartingPoint,
   11161             :                                      CSLConstList papszOptions)
   11162             : {
   11163          19 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11164          19 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11165          19 :     VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
   11166          19 :     auto array = hGroup->m_poImpl->ResolveMDArray(
   11167          57 :         std::string(pszName), std::string(pszStartingPoint), papszOptions);
   11168          19 :     if (!array)
   11169           2 :         return nullptr;
   11170          17 :     return new GDALMDArrayHS(array);
   11171             : }
   11172             : 
   11173             : /************************************************************************/
   11174             : /*                        GDALGroupGetGroupNames()                      */
   11175             : /************************************************************************/
   11176             : 
   11177             : /** Return the list of sub-groups contained in this group.
   11178             :  *
   11179             :  * This is the same as the C++ method GDALGroup::GetGroupNames().
   11180             :  *
   11181             :  * @return the group names, to be freed with CSLDestroy()
   11182             :  */
   11183          95 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
   11184             : {
   11185          95 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11186         190 :     auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
   11187         190 :     CPLStringList res;
   11188         215 :     for (const auto &name : names)
   11189             :     {
   11190         120 :         res.AddString(name.c_str());
   11191             :     }
   11192          95 :     return res.StealList();
   11193             : }
   11194             : 
   11195             : /************************************************************************/
   11196             : /*                           GDALGroupOpenGroup()                       */
   11197             : /************************************************************************/
   11198             : 
   11199             : /** Open and return a sub-group.
   11200             :  *
   11201             :  * This is the same as the C++ method GDALGroup::OpenGroup().
   11202             :  *
   11203             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11204             :  */
   11205         157 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11206             :                               CSLConstList papszOptions)
   11207             : {
   11208         157 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11209         157 :     VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
   11210             :     auto subGroup =
   11211         471 :         hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
   11212         157 :     if (!subGroup)
   11213          28 :         return nullptr;
   11214         129 :     return new GDALGroupHS(subGroup);
   11215             : }
   11216             : 
   11217             : /************************************************************************/
   11218             : /*                   GDALGroupGetVectorLayerNames()                     */
   11219             : /************************************************************************/
   11220             : 
   11221             : /** Return the list of layer names contained in this group.
   11222             :  *
   11223             :  * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
   11224             :  *
   11225             :  * @return the group names, to be freed with CSLDestroy()
   11226             :  * @since 3.4
   11227             :  */
   11228           8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
   11229             :                                     CSLConstList papszOptions)
   11230             : {
   11231           8 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11232          16 :     auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
   11233          16 :     CPLStringList res;
   11234          18 :     for (const auto &name : names)
   11235             :     {
   11236          10 :         res.AddString(name.c_str());
   11237             :     }
   11238           8 :     return res.StealList();
   11239             : }
   11240             : 
   11241             : /************************************************************************/
   11242             : /*                      GDALGroupOpenVectorLayer()                      */
   11243             : /************************************************************************/
   11244             : 
   11245             : /** Open and return a vector layer.
   11246             :  *
   11247             :  * This is the same as the C++ method GDALGroup::OpenVectorLayer().
   11248             :  *
   11249             :  * Note that the vector layer is owned by its parent GDALDatasetH, and thus
   11250             :  * the returned handled if only valid while the parent GDALDatasetH is kept
   11251             :  * opened.
   11252             :  *
   11253             :  * @return the vector layer, or nullptr.
   11254             :  * @since 3.4
   11255             :  */
   11256          12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
   11257             :                                    const char *pszVectorLayerName,
   11258             :                                    CSLConstList papszOptions)
   11259             : {
   11260          12 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11261          12 :     VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
   11262          24 :     return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
   11263          24 :         std::string(pszVectorLayerName), papszOptions));
   11264             : }
   11265             : 
   11266             : /************************************************************************/
   11267             : /*                       GDALGroupOpenMDArrayFromFullname()             */
   11268             : /************************************************************************/
   11269             : 
   11270             : /** Open and return a sub-group from its fully qualified name.
   11271             :  *
   11272             :  * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
   11273             :  *
   11274             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11275             :  *
   11276             :  * @since GDAL 3.2
   11277             :  */
   11278           3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
   11279             :                                           const char *pszFullname,
   11280             :                                           CSLConstList papszOptions)
   11281             : {
   11282           3 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11283           3 :     VALIDATE_POINTER1(pszFullname, __func__, nullptr);
   11284           3 :     auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
   11285           9 :         std::string(pszFullname), papszOptions);
   11286           3 :     if (!subGroup)
   11287           2 :         return nullptr;
   11288           1 :     return new GDALGroupHS(subGroup);
   11289             : }
   11290             : 
   11291             : /************************************************************************/
   11292             : /*                         GDALGroupGetDimensions()                     */
   11293             : /************************************************************************/
   11294             : 
   11295             : /** Return the list of dimensions contained in this group and used by its
   11296             :  * arrays.
   11297             :  *
   11298             :  * The returned array must be freed with GDALReleaseDimensions().  If only the
   11299             :  * array itself needs to be freed, CPLFree() should be called (and
   11300             :  * GDALDimensionRelease() on individual array members).
   11301             :  *
   11302             :  * This is the same as the C++ method GDALGroup::GetDimensions().
   11303             :  *
   11304             :  * @param hGroup Group.
   11305             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11306             :  * @param papszOptions Driver specific options determining how dimensions
   11307             :  * should be retrieved. Pass nullptr for default behavior.
   11308             :  *
   11309             :  * @return an array of *pnCount dimensions.
   11310             :  */
   11311          73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
   11312             :                                        CSLConstList papszOptions)
   11313             : {
   11314          73 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11315          73 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11316          73 :     auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
   11317             :     auto ret = static_cast<GDALDimensionH *>(
   11318          73 :         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
   11319         230 :     for (size_t i = 0; i < dims.size(); i++)
   11320             :     {
   11321         157 :         ret[i] = new GDALDimensionHS(dims[i]);
   11322             :     }
   11323          73 :     *pnCount = dims.size();
   11324          73 :     return ret;
   11325             : }
   11326             : 
   11327             : /************************************************************************/
   11328             : /*                          GDALGroupGetAttribute()                     */
   11329             : /************************************************************************/
   11330             : 
   11331             : /** Return an attribute by its name.
   11332             :  *
   11333             :  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
   11334             :  *
   11335             :  * The returned attribute must be freed with GDALAttributeRelease().
   11336             :  */
   11337          78 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
   11338             : {
   11339          78 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11340          78 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11341         234 :     auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
   11342          78 :     if (attr)
   11343          74 :         return new GDALAttributeHS(attr);
   11344           4 :     return nullptr;
   11345             : }
   11346             : 
   11347             : /************************************************************************/
   11348             : /*                         GDALGroupGetAttributes()                     */
   11349             : /************************************************************************/
   11350             : 
   11351             : /** Return the list of attributes contained in this group.
   11352             :  *
   11353             :  * The returned array must be freed with GDALReleaseAttributes(). If only the
   11354             :  * array itself needs to be freed, CPLFree() should be called (and
   11355             :  * GDALAttributeRelease() on individual array members).
   11356             :  *
   11357             :  * This is the same as the C++ method GDALGroup::GetAttributes().
   11358             :  *
   11359             :  * @param hGroup Group.
   11360             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11361             :  * @param papszOptions Driver specific options determining how attributes
   11362             :  * should be retrieved. Pass nullptr for default behavior.
   11363             :  *
   11364             :  * @return an array of *pnCount attributes.
   11365             :  */
   11366          67 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
   11367             :                                        CSLConstList papszOptions)
   11368             : {
   11369          67 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11370          67 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11371          67 :     auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
   11372             :     auto ret = static_cast<GDALAttributeH *>(
   11373          67 :         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
   11374         221 :     for (size_t i = 0; i < attrs.size(); i++)
   11375             :     {
   11376         154 :         ret[i] = new GDALAttributeHS(attrs[i]);
   11377             :     }
   11378          67 :     *pnCount = attrs.size();
   11379          67 :     return ret;
   11380             : }
   11381             : 
   11382             : /************************************************************************/
   11383             : /*                     GDALGroupGetStructuralInfo()                     */
   11384             : /************************************************************************/
   11385             : 
   11386             : /** Return structural information on the group.
   11387             :  *
   11388             :  * This may be the compression, etc..
   11389             :  *
   11390             :  * The return value should not be freed and is valid until GDALGroup is
   11391             :  * released or this function called again.
   11392             :  *
   11393             :  * This is the same as the C++ method GDALGroup::GetStructuralInfo().
   11394             :  */
   11395           4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
   11396             : {
   11397           4 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11398           4 :     return hGroup->m_poImpl->GetStructuralInfo();
   11399             : }
   11400             : 
   11401             : /************************************************************************/
   11402             : /*                         GDALReleaseAttributes()                      */
   11403             : /************************************************************************/
   11404             : 
   11405             : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
   11406             :  *
   11407             :  * @param attributes return pointer of above methods
   11408             :  * @param nCount *pnCount value returned by above methods
   11409             :  */
   11410         125 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
   11411             : {
   11412         408 :     for (size_t i = 0; i < nCount; i++)
   11413             :     {
   11414         283 :         delete attributes[i];
   11415             :     }
   11416         125 :     CPLFree(attributes);
   11417         125 : }
   11418             : 
   11419             : /************************************************************************/
   11420             : /*                         GDALGroupCreateGroup()                       */
   11421             : /************************************************************************/
   11422             : 
   11423             : /** Create a sub-group within a group.
   11424             :  *
   11425             :  * This is the same as the C++ method GDALGroup::CreateGroup().
   11426             :  *
   11427             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11428             :  */
   11429         173 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11430             :                                 CSLConstList papszOptions)
   11431             : {
   11432         173 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11433         173 :     VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
   11434         519 :     auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
   11435         519 :                                              papszOptions);
   11436         173 :     if (!ret)
   11437          49 :         return nullptr;
   11438         124 :     return new GDALGroupHS(ret);
   11439             : }
   11440             : 
   11441             : /************************************************************************/
   11442             : /*                         GDALGroupDeleteGroup()                       */
   11443             : /************************************************************************/
   11444             : 
   11445             : /** Delete a sub-group from a group.
   11446             :  *
   11447             :  * After this call, if a previously obtained instance of the deleted object
   11448             :  * is still alive, no method other than for freeing it should be invoked.
   11449             :  *
   11450             :  * This is the same as the C++ method GDALGroup::DeleteGroup().
   11451             :  *
   11452             :  * @return true in case of success.
   11453             :  * @since GDAL 3.8
   11454             :  */
   11455          20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11456             :                           CSLConstList papszOptions)
   11457             : {
   11458          20 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11459          20 :     VALIDATE_POINTER1(pszSubGroupName, __func__, false);
   11460          40 :     return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
   11461          20 :                                          papszOptions);
   11462             : }
   11463             : 
   11464             : /************************************************************************/
   11465             : /*                      GDALGroupCreateDimension()                      */
   11466             : /************************************************************************/
   11467             : 
   11468             : /** Create a dimension within a group.
   11469             :  *
   11470             :  * This is the same as the C++ method GDALGroup::CreateDimension().
   11471             :  *
   11472             :  * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
   11473             :  */
   11474         658 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
   11475             :                                         const char *pszType,
   11476             :                                         const char *pszDirection, GUInt64 nSize,
   11477             :                                         CSLConstList papszOptions)
   11478             : {
   11479         658 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11480         658 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11481         658 :     auto ret = hGroup->m_poImpl->CreateDimension(
   11482        1316 :         std::string(pszName), std::string(pszType ? pszType : ""),
   11483        2632 :         std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
   11484         658 :     if (!ret)
   11485           9 :         return nullptr;
   11486         649 :     return new GDALDimensionHS(ret);
   11487             : }
   11488             : 
   11489             : /************************************************************************/
   11490             : /*                      GDALGroupCreateMDArray()                        */
   11491             : /************************************************************************/
   11492             : 
   11493             : /** Create a multidimensional array within a group.
   11494             :  *
   11495             :  * This is the same as the C++ method GDALGroup::CreateMDArray().
   11496             :  *
   11497             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11498             :  */
   11499         599 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
   11500             :                                     size_t nDimensions,
   11501             :                                     GDALDimensionH *pahDimensions,
   11502             :                                     GDALExtendedDataTypeH hEDT,
   11503             :                                     CSLConstList papszOptions)
   11504             : {
   11505         599 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11506         599 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11507         599 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11508        1198 :     std::vector<std::shared_ptr<GDALDimension>> dims;
   11509         599 :     dims.reserve(nDimensions);
   11510        1413 :     for (size_t i = 0; i < nDimensions; i++)
   11511         814 :         dims.push_back(pahDimensions[i]->m_poImpl);
   11512        1797 :     auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
   11513        1797 :                                                *(hEDT->m_poImpl), papszOptions);
   11514         599 :     if (!ret)
   11515          65 :         return nullptr;
   11516         534 :     return new GDALMDArrayHS(ret);
   11517             : }
   11518             : 
   11519             : /************************************************************************/
   11520             : /*                         GDALGroupDeleteMDArray()                     */
   11521             : /************************************************************************/
   11522             : 
   11523             : /** Delete an array from a group.
   11524             :  *
   11525             :  * After this call, if a previously obtained instance of the deleted object
   11526             :  * is still alive, no method other than for freeing it should be invoked.
   11527             :  *
   11528             :  * This is the same as the C++ method GDALGroup::DeleteMDArray().
   11529             :  *
   11530             :  * @return true in case of success.
   11531             :  * @since GDAL 3.8
   11532             :  */
   11533          20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
   11534             :                             CSLConstList papszOptions)
   11535             : {
   11536          20 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11537          20 :     VALIDATE_POINTER1(pszName, __func__, false);
   11538          20 :     return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
   11539             : }
   11540             : 
   11541             : /************************************************************************/
   11542             : /*                      GDALGroupCreateAttribute()                      */
   11543             : /************************************************************************/
   11544             : 
   11545             : /** Create a attribute within a group.
   11546             :  *
   11547             :  * This is the same as the C++ method GDALGroup::CreateAttribute().
   11548             :  *
   11549             :  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
   11550             :  */
   11551         120 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
   11552             :                                         size_t nDimensions,
   11553             :                                         const GUInt64 *panDimensions,
   11554             :                                         GDALExtendedDataTypeH hEDT,
   11555             :                                         CSLConstList papszOptions)
   11556             : {
   11557         120 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11558         120 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11559         240 :     std::vector<GUInt64> dims;
   11560         120 :     dims.reserve(nDimensions);
   11561         166 :     for (size_t i = 0; i < nDimensions; i++)
   11562          46 :         dims.push_back(panDimensions[i]);
   11563         120 :     auto ret = hGroup->m_poImpl->CreateAttribute(
   11564         360 :         std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
   11565         120 :     if (!ret)
   11566          14 :         return nullptr;
   11567         106 :     return new GDALAttributeHS(ret);
   11568             : }
   11569             : 
   11570             : /************************************************************************/
   11571             : /*                         GDALGroupDeleteAttribute()                   */
   11572             : /************************************************************************/
   11573             : 
   11574             : /** Delete an attribute from a group.
   11575             :  *
   11576             :  * After this call, if a previously obtained instance of the deleted object
   11577             :  * is still alive, no method other than for freeing it should be invoked.
   11578             :  *
   11579             :  * This is the same as the C++ method GDALGroup::DeleteAttribute().
   11580             :  *
   11581             :  * @return true in case of success.
   11582             :  * @since GDAL 3.8
   11583             :  */
   11584          25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
   11585             :                               CSLConstList papszOptions)
   11586             : {
   11587          25 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11588          25 :     VALIDATE_POINTER1(pszName, __func__, false);
   11589          50 :     return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
   11590          25 :                                              papszOptions);
   11591             : }
   11592             : 
   11593             : /************************************************************************/
   11594             : /*                          GDALGroupRename()                           */
   11595             : /************************************************************************/
   11596             : 
   11597             : /** Rename the group.
   11598             :  *
   11599             :  * This is not implemented by all drivers.
   11600             :  *
   11601             :  * Drivers known to implement it: MEM, netCDF.
   11602             :  *
   11603             :  * This is the same as the C++ method GDALGroup::Rename()
   11604             :  *
   11605             :  * @return true in case of success
   11606             :  * @since GDAL 3.8
   11607             :  */
   11608          45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
   11609             : {
   11610          45 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11611          45 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   11612          45 :     return hGroup->m_poImpl->Rename(pszNewName);
   11613             : }
   11614             : 
   11615             : /************************************************************************/
   11616             : /*                 GDALGroupSubsetDimensionFromSelection()              */
   11617             : /************************************************************************/
   11618             : 
   11619             : /** Return a virtual group whose one dimension has been subset according to a
   11620             :  * selection.
   11621             :  *
   11622             :  * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
   11623             :  *
   11624             :  * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
   11625             :  */
   11626             : GDALGroupH
   11627          14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
   11628             :                                       const char *pszSelection,
   11629             :                                       CPL_UNUSED CSLConstList papszOptions)
   11630             : {
   11631          14 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11632          14 :     VALIDATE_POINTER1(pszSelection, __func__, nullptr);
   11633          14 :     auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
   11634          42 :         std::string(pszSelection));
   11635          14 :     if (!hNewGroup)
   11636           8 :         return nullptr;
   11637           6 :     return new GDALGroupHS(hNewGroup);
   11638             : }
   11639             : 
   11640             : /************************************************************************/
   11641             : /*                        GDALMDArrayRelease()                          */
   11642             : /************************************************************************/
   11643             : 
   11644             : /** Release the GDAL in-memory object associated with a GDALMDArray.
   11645             :  *
   11646             :  * Note: when applied on a object coming from a driver, this does not
   11647             :  * destroy the object in the file, database, etc...
   11648             :  */
   11649        1964 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
   11650             : {
   11651        1964 :     delete hMDArray;
   11652        1964 : }
   11653             : 
   11654             : /************************************************************************/
   11655             : /*                        GDALMDArrayGetName()                          */
   11656             : /************************************************************************/
   11657             : 
   11658             : /** Return array name.
   11659             :  *
   11660             :  * This is the same as the C++ method GDALMDArray::GetName()
   11661             :  */
   11662          83 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
   11663             : {
   11664          83 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11665          83 :     return hArray->m_poImpl->GetName().c_str();
   11666             : }
   11667             : 
   11668             : /************************************************************************/
   11669             : /*                    GDALMDArrayGetFullName()                          */
   11670             : /************************************************************************/
   11671             : 
   11672             : /** Return array full name.
   11673             :  *
   11674             :  * This is the same as the C++ method GDALMDArray::GetFullName()
   11675             :  */
   11676          50 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
   11677             : {
   11678          50 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11679          50 :     return hArray->m_poImpl->GetFullName().c_str();
   11680             : }
   11681             : 
   11682             : /************************************************************************/
   11683             : /*                        GDALMDArrayGetName()                          */
   11684             : /************************************************************************/
   11685             : 
   11686             : /** Return the total number of values in the array.
   11687             :  *
   11688             :  * This is the same as the C++ method
   11689             :  * GDALAbstractMDArray::GetTotalElementsCount()
   11690             :  */
   11691           6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
   11692             : {
   11693           6 :     VALIDATE_POINTER1(hArray, __func__, 0);
   11694           6 :     return hArray->m_poImpl->GetTotalElementsCount();
   11695             : }
   11696             : 
   11697             : /************************************************************************/
   11698             : /*                        GDALMDArrayGetDimensionCount()                */
   11699             : /************************************************************************/
   11700             : 
   11701             : /** Return the number of dimensions.
   11702             :  *
   11703             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
   11704             :  */
   11705       10157 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
   11706             : {
   11707       10157 :     VALIDATE_POINTER1(hArray, __func__, 0);
   11708       10157 :     return hArray->m_poImpl->GetDimensionCount();
   11709             : }
   11710             : 
   11711             : /************************************************************************/
   11712             : /*                        GDALMDArrayGetDimensions()                    */
   11713             : /************************************************************************/
   11714             : 
   11715             : /** Return the dimensions of the array
   11716             :  *
   11717             :  * The returned array must be freed with GDALReleaseDimensions(). If only the
   11718             :  * array itself needs to be freed, CPLFree() should be called (and
   11719             :  * GDALDimensionRelease() on individual array members).
   11720             :  *
   11721             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
   11722             :  *
   11723             :  * @param hArray Array.
   11724             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11725             :  *
   11726             :  * @return an array of *pnCount dimensions.
   11727             :  */
   11728        2249 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
   11729             : {
   11730        2249 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11731        2249 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11732        2249 :     const auto &dims(hArray->m_poImpl->GetDimensions());
   11733             :     auto ret = static_cast<GDALDimensionH *>(
   11734        2249 :         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
   11735        6355 :     for (size_t i = 0; i < dims.size(); i++)
   11736             :     {
   11737        4106 :         ret[i] = new GDALDimensionHS(dims[i]);
   11738             :     }
   11739        2249 :     *pnCount = dims.size();
   11740        2249 :     return ret;
   11741             : }
   11742             : 
   11743             : /************************************************************************/
   11744             : /*                        GDALReleaseDimensions()                       */
   11745             : /************************************************************************/
   11746             : 
   11747             : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
   11748             :  *
   11749             :  * @param dims return pointer of above methods
   11750             :  * @param nCount *pnCount value returned by above methods
   11751             :  */
   11752        2322 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
   11753             : {
   11754        6585 :     for (size_t i = 0; i < nCount; i++)
   11755             :     {
   11756        4263 :         delete dims[i];
   11757             :     }
   11758        2322 :     CPLFree(dims);
   11759        2322 : }
   11760             : 
   11761             : /************************************************************************/
   11762             : /*                        GDALMDArrayGetDataType()                     */
   11763             : /************************************************************************/
   11764             : 
   11765             : /** Return the data type
   11766             :  *
   11767             :  * The return must be freed with GDALExtendedDataTypeRelease().
   11768             :  */
   11769        3819 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
   11770             : {
   11771        3819 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11772             :     return new GDALExtendedDataTypeHS(
   11773        3819 :         new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
   11774             : }
   11775             : 
   11776             : /************************************************************************/
   11777             : /*                          GDALMDArrayRead()                           */
   11778             : /************************************************************************/
   11779             : 
   11780             : /** Read part or totality of a multidimensional array.
   11781             :  *
   11782             :  * This is the same as the C++ method GDALAbstractMDArray::Read()
   11783             :  *
   11784             :  * @return TRUE in case of success.
   11785             :  */
   11786        1916 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   11787             :                     const size_t *count, const GInt64 *arrayStep,
   11788             :                     const GPtrDiff_t *bufferStride,
   11789             :                     GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
   11790             :                     const void *pDstBufferAllocStart,
   11791             :                     size_t nDstBufferAllocSize)
   11792             : {
   11793        1916 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11794        1916 :     if ((arrayStartIdx == nullptr || count == nullptr) &&
   11795           0 :         hArray->m_poImpl->GetDimensionCount() > 0)
   11796             :     {
   11797           0 :         VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
   11798           0 :         VALIDATE_POINTER1(count, __func__, FALSE);
   11799             :     }
   11800        1916 :     VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
   11801        1916 :     VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
   11802             :     // coverity[var_deref_model]
   11803        3832 :     return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
   11804        1916 :                                   *(bufferDataType->m_poImpl), pDstBuffer,
   11805        1916 :                                   pDstBufferAllocStart, nDstBufferAllocSize);
   11806             : }
   11807             : 
   11808             : /************************************************************************/
   11809             : /*                          GDALMDArrayWrite()                           */
   11810             : /************************************************************************/
   11811             : 
   11812             : /** Write part or totality of a multidimensional array.
   11813             :  *
   11814             :  * This is the same as the C++ method GDALAbstractMDArray::Write()
   11815             :  *
   11816             :  * @return TRUE in case of success.
   11817             :  */
   11818         554 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   11819             :                      const size_t *count, const GInt64 *arrayStep,
   11820             :                      const GPtrDiff_t *bufferStride,
   11821             :                      GDALExtendedDataTypeH bufferDataType,
   11822             :                      const void *pSrcBuffer, const void *pSrcBufferAllocStart,
   11823             :                      size_t nSrcBufferAllocSize)
   11824             : {
   11825         554 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11826         554 :     if ((arrayStartIdx == nullptr || count == nullptr) &&
   11827           0 :         hArray->m_poImpl->GetDimensionCount() > 0)
   11828             :     {
   11829           0 :         VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
   11830           0 :         VALIDATE_POINTER1(count, __func__, FALSE);
   11831             :     }
   11832         554 :     VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
   11833         554 :     VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
   11834             :     // coverity[var_deref_model]
   11835        1108 :     return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
   11836         554 :                                    bufferStride, *(bufferDataType->m_poImpl),
   11837             :                                    pSrcBuffer, pSrcBufferAllocStart,
   11838         554 :                                    nSrcBufferAllocSize);
   11839             : }
   11840             : 
   11841             : /************************************************************************/
   11842             : /*                       GDALMDArrayAdviseRead()                        */
   11843             : /************************************************************************/
   11844             : 
   11845             : /** Advise driver of upcoming read requests.
   11846             :  *
   11847             :  * This is the same as the C++ method GDALMDArray::AdviseRead()
   11848             :  *
   11849             :  * @return TRUE in case of success.
   11850             :  *
   11851             :  * @since GDAL 3.2
   11852             :  */
   11853           0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   11854             :                           const size_t *count)
   11855             : {
   11856           0 :     return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
   11857             : }
   11858             : 
   11859             : /************************************************************************/
   11860             : /*                      GDALMDArrayAdviseReadEx()                       */
   11861             : /************************************************************************/
   11862             : 
   11863             : /** Advise driver of upcoming read requests.
   11864             :  *
   11865             :  * This is the same as the C++ method GDALMDArray::AdviseRead()
   11866             :  *
   11867             :  * @return TRUE in case of success.
   11868             :  *
   11869             :  * @since GDAL 3.4
   11870             :  */
   11871          22 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   11872             :                             const size_t *count, CSLConstList papszOptions)
   11873             : {
   11874          22 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11875             :     // coverity[var_deref_model]
   11876          22 :     return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
   11877             : }
   11878             : 
   11879             : /************************************************************************/
   11880             : /*                         GDALMDArrayGetAttribute()                    */
   11881             : /************************************************************************/
   11882             : 
   11883             : /** Return an attribute by its name.
   11884             :  *
   11885             :  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
   11886             :  *
   11887             :  * The returned attribute must be freed with GDALAttributeRelease().
   11888             :  */
   11889         119 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
   11890             : {
   11891         119 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11892         119 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11893         357 :     auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
   11894         119 :     if (attr)
   11895         110 :         return new GDALAttributeHS(attr);
   11896           9 :     return nullptr;
   11897             : }
   11898             : 
   11899             : /************************************************************************/
   11900             : /*                        GDALMDArrayGetAttributes()                    */
   11901             : /************************************************************************/
   11902             : 
   11903             : /** Return the list of attributes contained in this array.
   11904             :  *
   11905             :  * The returned array must be freed with GDALReleaseAttributes(). If only the
   11906             :  * array itself needs to be freed, CPLFree() should be called (and
   11907             :  * GDALAttributeRelease() on individual array members).
   11908             :  *
   11909             :  * This is the same as the C++ method GDALMDArray::GetAttributes().
   11910             :  *
   11911             :  * @param hArray Array.
   11912             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11913             :  * @param papszOptions Driver specific options determining how attributes
   11914             :  * should be retrieved. Pass nullptr for default behavior.
   11915             :  *
   11916             :  * @return an array of *pnCount attributes.
   11917             :  */
   11918          58 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
   11919             :                                          CSLConstList papszOptions)
   11920             : {
   11921          58 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11922          58 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11923          58 :     auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
   11924             :     auto ret = static_cast<GDALAttributeH *>(
   11925          58 :         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
   11926         187 :     for (size_t i = 0; i < attrs.size(); i++)
   11927             :     {
   11928         129 :         ret[i] = new GDALAttributeHS(attrs[i]);
   11929             :     }
   11930          58 :     *pnCount = attrs.size();
   11931          58 :     return ret;
   11932             : }
   11933             : 
   11934             : /************************************************************************/
   11935             : /*                       GDALMDArrayCreateAttribute()                   */
   11936             : /************************************************************************/
   11937             : 
   11938             : /** Create a attribute within an array.
   11939             :  *
   11940             :  * This is the same as the C++ method GDALMDArray::CreateAttribute().
   11941             :  *
   11942             :  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
   11943             :  */
   11944         150 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
   11945             :                                           const char *pszName,
   11946             :                                           size_t nDimensions,
   11947             :                                           const GUInt64 *panDimensions,
   11948             :                                           GDALExtendedDataTypeH hEDT,
   11949             :                                           CSLConstList papszOptions)
   11950             : {
   11951         150 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11952         150 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11953         150 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11954         300 :     std::vector<GUInt64> dims;
   11955         150 :     dims.reserve(nDimensions);
   11956         175 :     for (size_t i = 0; i < nDimensions; i++)
   11957          25 :         dims.push_back(panDimensions[i]);
   11958         150 :     auto ret = hArray->m_poImpl->CreateAttribute(
   11959         450 :         std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
   11960         150 :     if (!ret)
   11961           9 :         return nullptr;
   11962         141 :     return new GDALAttributeHS(ret);
   11963             : }
   11964             : 
   11965             : /************************************************************************/
   11966             : /*                       GDALMDArrayDeleteAttribute()                   */
   11967             : /************************************************************************/
   11968             : 
   11969             : /** Delete an attribute from an array.
   11970             :  *
   11971             :  * After this call, if a previously obtained instance of the deleted object
   11972             :  * is still alive, no method other than for freeing it should be invoked.
   11973             :  *
   11974             :  * This is the same as the C++ method GDALMDArray::DeleteAttribute().
   11975             :  *
   11976             :  * @return true in case of success.
   11977             :  * @since GDAL 3.8
   11978             :  */
   11979          24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
   11980             :                                 CSLConstList papszOptions)
   11981             : {
   11982          24 :     VALIDATE_POINTER1(hArray, __func__, false);
   11983          24 :     VALIDATE_POINTER1(pszName, __func__, false);
   11984          48 :     return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
   11985          24 :                                              papszOptions);
   11986             : }
   11987             : 
   11988             : /************************************************************************/
   11989             : /*                       GDALMDArrayGetRawNoDataValue()                 */
   11990             : /************************************************************************/
   11991             : 
   11992             : /** Return the nodata value as a "raw" value.
   11993             :  *
   11994             :  * The value returned might be nullptr in case of no nodata value. When
   11995             :  * a nodata value is registered, a non-nullptr will be returned whose size in
   11996             :  * bytes is GetDataType().GetSize().
   11997             :  *
   11998             :  * The returned value should not be modified or freed.
   11999             :  *
   12000             :  * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
   12001             :  *
   12002             :  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
   12003             :  */
   12004          74 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
   12005             : {
   12006          74 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12007          74 :     return hArray->m_poImpl->GetRawNoDataValue();
   12008             : }
   12009             : 
   12010             : /************************************************************************/
   12011             : /*                      GDALMDArrayGetNoDataValueAsDouble()             */
   12012             : /************************************************************************/
   12013             : 
   12014             : /** Return the nodata value as a double.
   12015             :  *
   12016             :  * The value returned might be nullptr in case of no nodata value. When
   12017             :  * a nodata value is registered, a non-nullptr will be returned whose size in
   12018             :  * bytes is GetDataType().GetSize().
   12019             :  *
   12020             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
   12021             :  *
   12022             :  * @param hArray Array handle.
   12023             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12024             :  * if a nodata value exists and can be converted to double. Might be nullptr.
   12025             :  *
   12026             :  * @return the nodata value as a double. A 0.0 value might also indicate the
   12027             :  * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
   12028             :  * will be set to false then).
   12029             :  */
   12030         118 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
   12031             :                                          int *pbHasNoDataValue)
   12032             : {
   12033         118 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12034         118 :     bool bHasNodataValue = false;
   12035         118 :     double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
   12036         118 :     if (pbHasNoDataValue)
   12037         118 :         *pbHasNoDataValue = bHasNodataValue;
   12038         118 :     return ret;
   12039             : }
   12040             : 
   12041             : /************************************************************************/
   12042             : /*                      GDALMDArrayGetNoDataValueAsInt64()              */
   12043             : /************************************************************************/
   12044             : 
   12045             : /** Return the nodata value as a Int64.
   12046             :  *
   12047             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
   12048             :  *
   12049             :  * @param hArray Array handle.
   12050             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12051             :  * if a nodata value exists and can be converted to Int64. Might be nullptr.
   12052             :  *
   12053             :  * @return the nodata value as a Int64.
   12054             :  * @since GDAL 3.5
   12055             :  */
   12056          11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
   12057             :                                          int *pbHasNoDataValue)
   12058             : {
   12059          11 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12060          11 :     bool bHasNodataValue = false;
   12061          11 :     const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
   12062          11 :     if (pbHasNoDataValue)
   12063          11 :         *pbHasNoDataValue = bHasNodataValue;
   12064          11 :     return ret;
   12065             : }
   12066             : 
   12067             : /************************************************************************/
   12068             : /*                      GDALMDArrayGetNoDataValueAsUInt64()              */
   12069             : /************************************************************************/
   12070             : 
   12071             : /** Return the nodata value as a UInt64.
   12072             :  *
   12073             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
   12074             :  *
   12075             :  * @param hArray Array handle.
   12076             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   12077             :  * if a nodata value exists and can be converted to UInt64. Might be nullptr.
   12078             :  *
   12079             :  * @return the nodata value as a UInt64.
   12080             :  * @since GDAL 3.5
   12081             :  */
   12082           7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
   12083             :                                            int *pbHasNoDataValue)
   12084             : {
   12085           7 :     VALIDATE_POINTER1(hArray, __func__, 0);
   12086           7 :     bool bHasNodataValue = false;
   12087           7 :     const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
   12088           7 :     if (pbHasNoDataValue)
   12089           7 :         *pbHasNoDataValue = bHasNodataValue;
   12090           7 :     return ret;
   12091             : }
   12092             : 
   12093             : /************************************************************************/
   12094             : /*                     GDALMDArraySetRawNoDataValue()                   */
   12095             : /************************************************************************/
   12096             : 
   12097             : /** Set the nodata value as a "raw" value.
   12098             :  *
   12099             :  * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
   12100             :  * void*).
   12101             :  *
   12102             :  * @return TRUE in case of success.
   12103             :  */
   12104          14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
   12105             : {
   12106          14 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12107          14 :     return hArray->m_poImpl->SetRawNoDataValue(pNoData);
   12108             : }
   12109             : 
   12110             : /************************************************************************/
   12111             : /*                   GDALMDArraySetNoDataValueAsDouble()                */
   12112             : /************************************************************************/
   12113             : 
   12114             : /** Set the nodata value as a double.
   12115             :  *
   12116             :  * If the natural data type of the attribute/array is not double, type
   12117             :  * conversion will occur to the type returned by GetDataType().
   12118             :  *
   12119             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
   12120             :  *
   12121             :  * @return TRUE in case of success.
   12122             :  */
   12123          51 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
   12124             : {
   12125          51 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12126          51 :     return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
   12127             : }
   12128             : 
   12129             : /************************************************************************/
   12130             : /*                   GDALMDArraySetNoDataValueAsInt64()                 */
   12131             : /************************************************************************/
   12132             : 
   12133             : /** Set the nodata value as a Int64.
   12134             :  *
   12135             :  * If the natural data type of the attribute/array is not Int64, type conversion
   12136             :  * will occur to the type returned by GetDataType().
   12137             :  *
   12138             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
   12139             :  *
   12140             :  * @return TRUE in case of success.
   12141             :  * @since GDAL 3.5
   12142             :  */
   12143           1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
   12144             : {
   12145           1 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12146           1 :     return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
   12147             : }
   12148             : 
   12149             : /************************************************************************/
   12150             : /*                   GDALMDArraySetNoDataValueAsUInt64()                */
   12151             : /************************************************************************/
   12152             : 
   12153             : /** Set the nodata value as a UInt64.
   12154             :  *
   12155             :  * If the natural data type of the attribute/array is not UInt64, type
   12156             :  * conversion will occur to the type returned by GetDataType().
   12157             :  *
   12158             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
   12159             :  *
   12160             :  * @return TRUE in case of success.
   12161             :  * @since GDAL 3.5
   12162             :  */
   12163           1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
   12164             :                                       uint64_t nNoDataValue)
   12165             : {
   12166           1 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12167           1 :     return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
   12168             : }
   12169             : 
   12170             : /************************************************************************/
   12171             : /*                        GDALMDArrayResize()                           */
   12172             : /************************************************************************/
   12173             : 
   12174             : /** Resize an array to new dimensions.
   12175             :  *
   12176             :  * Not all drivers may allow this operation, and with restrictions (e.g.
   12177             :  * for netCDF, this is limited to growing of "unlimited" dimensions)
   12178             :  *
   12179             :  * Resizing a dimension used in other arrays will cause those other arrays
   12180             :  * to be resized.
   12181             :  *
   12182             :  * This is the same as the C++ method GDALMDArray::Resize().
   12183             :  *
   12184             :  * @param hArray Array.
   12185             :  * @param panNewDimSizes Array of GetDimensionCount() values containing the
   12186             :  *                       new size of each indexing dimension.
   12187             :  * @param papszOptions Options. (Driver specific)
   12188             :  * @return true in case of success.
   12189             :  * @since GDAL 3.7
   12190             :  */
   12191          42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
   12192             :                        CSLConstList papszOptions)
   12193             : {
   12194          42 :     VALIDATE_POINTER1(hArray, __func__, false);
   12195          42 :     VALIDATE_POINTER1(panNewDimSizes, __func__, false);
   12196          84 :     std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
   12197         125 :     for (size_t i = 0; i < anNewDimSizes.size(); ++i)
   12198             :     {
   12199          83 :         anNewDimSizes[i] = panNewDimSizes[i];
   12200             :     }
   12201          42 :     return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
   12202             : }
   12203             : 
   12204             : /************************************************************************/
   12205             : /*                          GDALMDArraySetScale()                       */
   12206             : /************************************************************************/
   12207             : 
   12208             : /** Set the scale value to apply to raw values.
   12209             :  *
   12210             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12211             :  *
   12212             :  * This is the same as the C++ method GDALMDArray::SetScale().
   12213             :  *
   12214             :  * @return TRUE in case of success.
   12215             :  */
   12216           0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
   12217             : {
   12218           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12219           0 :     return hArray->m_poImpl->SetScale(dfScale);
   12220             : }
   12221             : 
   12222             : /************************************************************************/
   12223             : /*                        GDALMDArraySetScaleEx()                       */
   12224             : /************************************************************************/
   12225             : 
   12226             : /** Set the scale value to apply to raw values.
   12227             :  *
   12228             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12229             :  *
   12230             :  * This is the same as the C++ method GDALMDArray::SetScale().
   12231             :  *
   12232             :  * @return TRUE in case of success.
   12233             :  * @since GDAL 3.3
   12234             :  */
   12235          21 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
   12236             :                           GDALDataType eStorageType)
   12237             : {
   12238          21 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12239          21 :     return hArray->m_poImpl->SetScale(dfScale, eStorageType);
   12240             : }
   12241             : 
   12242             : /************************************************************************/
   12243             : /*                          GDALMDArraySetOffset()                       */
   12244             : /************************************************************************/
   12245             : 
   12246             : /** Set the scale value to apply to raw values.
   12247             :  *
   12248             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12249             :  *
   12250             :  * This is the same as the C++ method GDALMDArray::SetOffset().
   12251             :  *
   12252             :  * @return TRUE in case of success.
   12253             :  */
   12254           0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
   12255             : {
   12256           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12257           0 :     return hArray->m_poImpl->SetOffset(dfOffset);
   12258             : }
   12259             : 
   12260             : /************************************************************************/
   12261             : /*                       GDALMDArraySetOffsetEx()                       */
   12262             : /************************************************************************/
   12263             : 
   12264             : /** Set the scale value to apply to raw values.
   12265             :  *
   12266             :  * unscaled_value = raw_value * GetOffset() + GetOffset()
   12267             :  *
   12268             :  * This is the same as the C++ method GDALMDArray::SetOffset().
   12269             :  *
   12270             :  * @return TRUE in case of success.
   12271             :  * @since GDAL 3.3
   12272             :  */
   12273          21 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
   12274             :                            GDALDataType eStorageType)
   12275             : {
   12276          21 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12277          21 :     return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
   12278             : }
   12279             : 
   12280             : /************************************************************************/
   12281             : /*                          GDALMDArrayGetScale()                       */
   12282             : /************************************************************************/
   12283             : 
   12284             : /** Get the scale value to apply to raw values.
   12285             :  *
   12286             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12287             :  *
   12288             :  * This is the same as the C++ method GDALMDArray::GetScale().
   12289             :  *
   12290             :  * @return the scale value
   12291             :  */
   12292         103 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
   12293             : {
   12294         103 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12295         103 :     bool bHasValue = false;
   12296         103 :     double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
   12297         103 :     if (pbHasValue)
   12298         103 :         *pbHasValue = bHasValue;
   12299         103 :     return dfRet;
   12300             : }
   12301             : 
   12302             : /************************************************************************/
   12303             : /*                        GDALMDArrayGetScaleEx()                       */
   12304             : /************************************************************************/
   12305             : 
   12306             : /** Get the scale value to apply to raw values.
   12307             :  *
   12308             :  * unscaled_value = raw_value * GetScale() + GetScale()
   12309             :  *
   12310             :  * This is the same as the C++ method GDALMDArray::GetScale().
   12311             :  *
   12312             :  * @return the scale value
   12313             :  * @since GDAL 3.3
   12314             :  */
   12315           5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
   12316             :                              GDALDataType *peStorageType)
   12317             : {
   12318           5 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12319           5 :     bool bHasValue = false;
   12320           5 :     double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
   12321           5 :     if (pbHasValue)
   12322           5 :         *pbHasValue = bHasValue;
   12323           5 :     return dfRet;
   12324             : }
   12325             : 
   12326             : /************************************************************************/
   12327             : /*                          GDALMDArrayGetOffset()                      */
   12328             : /************************************************************************/
   12329             : 
   12330             : /** Get the scale value to apply to raw values.
   12331             :  *
   12332             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12333             :  *
   12334             :  * This is the same as the C++ method GDALMDArray::GetOffset().
   12335             :  *
   12336             :  * @return the scale value
   12337             :  */
   12338         100 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
   12339             : {
   12340         100 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12341         100 :     bool bHasValue = false;
   12342         100 :     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
   12343         100 :     if (pbHasValue)
   12344         100 :         *pbHasValue = bHasValue;
   12345         100 :     return dfRet;
   12346             : }
   12347             : 
   12348             : /************************************************************************/
   12349             : /*                        GDALMDArrayGetOffsetEx()                      */
   12350             : /************************************************************************/
   12351             : 
   12352             : /** Get the scale value to apply to raw values.
   12353             :  *
   12354             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12355             :  *
   12356             :  * This is the same as the C++ method GDALMDArray::GetOffset().
   12357             :  *
   12358             :  * @return the scale value
   12359             :  * @since GDAL 3.3
   12360             :  */
   12361           5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
   12362             :                               GDALDataType *peStorageType)
   12363             : {
   12364           5 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12365           5 :     bool bHasValue = false;
   12366           5 :     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
   12367           5 :     if (pbHasValue)
   12368           5 :         *pbHasValue = bHasValue;
   12369           5 :     return dfRet;
   12370             : }
   12371             : 
   12372             : /************************************************************************/
   12373             : /*                      GDALMDArrayGetBlockSize()                       */
   12374             : /************************************************************************/
   12375             : 
   12376             : /** Return the "natural" block size of the array along all dimensions.
   12377             :  *
   12378             :  * Some drivers might organize the array in tiles/blocks and reading/writing
   12379             :  * aligned on those tile/block boundaries will be more efficient.
   12380             :  *
   12381             :  * The returned number of elements in the vector is the same as
   12382             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
   12383             :  * the natural block size along the considered dimension.
   12384             :  * "Flat" arrays will typically return a vector of values set to 0.
   12385             :  *
   12386             :  * The default implementation will return a vector of values set to 0.
   12387             :  *
   12388             :  * This method is used by GetProcessingChunkSize().
   12389             :  *
   12390             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
   12391             :  * theoretical case of a 32-bit platform, this might exceed its size_t
   12392             :  * allocation capabilities.
   12393             :  *
   12394             :  * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
   12395             :  *
   12396             :  * @return the block size, in number of elements along each dimension.
   12397             :  */
   12398          93 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
   12399             : {
   12400          93 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12401          93 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12402          93 :     auto res = hArray->m_poImpl->GetBlockSize();
   12403          93 :     auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
   12404         285 :     for (size_t i = 0; i < res.size(); i++)
   12405             :     {
   12406         192 :         ret[i] = res[i];
   12407             :     }
   12408          93 :     *pnCount = res.size();
   12409          93 :     return ret;
   12410             : }
   12411             : 
   12412             : /***********************************************************************/
   12413             : /*                   GDALMDArrayGetProcessingChunkSize()               */
   12414             : /************************************************************************/
   12415             : 
   12416             : /** \brief Return an optimal chunk size for read/write operations, given the
   12417             :  * natural block size and memory constraints specified.
   12418             :  *
   12419             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
   12420             :  * multiple of those returned by GetBlockSize() (unless the block define by
   12421             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
   12422             :  * returned by this method).
   12423             :  *
   12424             :  * This is the same as the C++ method
   12425             :  * GDALAbstractMDArray::GetProcessingChunkSize().
   12426             :  *
   12427             :  * @param hArray Array.
   12428             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12429             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
   12430             :  * chunk.
   12431             :  *
   12432             :  * @return the chunk size, in number of elements along each dimension.
   12433             :  */
   12434             : 
   12435           1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
   12436             :                                           size_t nMaxChunkMemory)
   12437             : {
   12438           1 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12439           1 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12440           1 :     auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
   12441           1 :     auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
   12442           3 :     for (size_t i = 0; i < res.size(); i++)
   12443             :     {
   12444           2 :         ret[i] = res[i];
   12445             :     }
   12446           1 :     *pnCount = res.size();
   12447           1 :     return ret;
   12448             : }
   12449             : 
   12450             : /************************************************************************/
   12451             : /*                     GDALMDArrayGetStructuralInfo()                   */
   12452             : /************************************************************************/
   12453             : 
   12454             : /** Return structural information on the array.
   12455             :  *
   12456             :  * This may be the compression, etc..
   12457             :  *
   12458             :  * The return value should not be freed and is valid until GDALMDArray is
   12459             :  * released or this function called again.
   12460             :  *
   12461             :  * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
   12462             :  */
   12463           6 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
   12464             : {
   12465           6 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12466           6 :     return hArray->m_poImpl->GetStructuralInfo();
   12467             : }
   12468             : 
   12469             : /************************************************************************/
   12470             : /*                        GDALMDArrayGetView()                          */
   12471             : /************************************************************************/
   12472             : 
   12473             : /** Return a view of the array using slicing or field access.
   12474             :  *
   12475             :  * The returned object should be released with GDALMDArrayRelease().
   12476             :  *
   12477             :  * This is the same as the C++ method GDALMDArray::GetView().
   12478             :  */
   12479         430 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
   12480             : {
   12481         430 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12482         430 :     VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
   12483        1290 :     auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
   12484         430 :     if (!sliced)
   12485          22 :         return nullptr;
   12486         408 :     return new GDALMDArrayHS(sliced);
   12487             : }
   12488             : 
   12489             : /************************************************************************/
   12490             : /*                       GDALMDArrayTranspose()                         */
   12491             : /************************************************************************/
   12492             : 
   12493             : /** Return a view of the array whose axis have been reordered.
   12494             :  *
   12495             :  * The returned object should be released with GDALMDArrayRelease().
   12496             :  *
   12497             :  * This is the same as the C++ method GDALMDArray::Transpose().
   12498             :  */
   12499          44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
   12500             :                                   const int *panMapNewAxisToOldAxis)
   12501             : {
   12502          44 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12503          88 :     std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
   12504          44 :     if (nNewAxisCount)
   12505             :     {
   12506          43 :         memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
   12507             :                nNewAxisCount * sizeof(int));
   12508             :     }
   12509          88 :     auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
   12510          44 :     if (!reordered)
   12511           7 :         return nullptr;
   12512          37 :     return new GDALMDArrayHS(reordered);
   12513             : }
   12514             : 
   12515             : /************************************************************************/
   12516             : /*                      GDALMDArrayGetUnscaled()                        */
   12517             : /************************************************************************/
   12518             : 
   12519             : /** Return an array that is the unscaled version of the current one.
   12520             :  *
   12521             :  * That is each value of the unscaled array will be
   12522             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12523             :  *
   12524             :  * Starting with GDAL 3.3, the Write() method is implemented and will convert
   12525             :  * from unscaled values to raw values.
   12526             :  *
   12527             :  * The returned object should be released with GDALMDArrayRelease().
   12528             :  *
   12529             :  * This is the same as the C++ method GDALMDArray::GetUnscaled().
   12530             :  */
   12531          13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
   12532             : {
   12533          13 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12534          26 :     auto unscaled = hArray->m_poImpl->GetUnscaled();
   12535          13 :     if (!unscaled)
   12536           0 :         return nullptr;
   12537          13 :     return new GDALMDArrayHS(unscaled);
   12538             : }
   12539             : 
   12540             : /************************************************************************/
   12541             : /*                          GDALMDArrayGetMask()                         */
   12542             : /************************************************************************/
   12543             : 
   12544             : /** Return an array that is a mask for the current array
   12545             :  *
   12546             :  * This array will be of type Byte, with values set to 0 to indicate invalid
   12547             :  * pixels of the current array, and values set to 1 to indicate valid pixels.
   12548             :  *
   12549             :  * The returned object should be released with GDALMDArrayRelease().
   12550             :  *
   12551             :  * This is the same as the C++ method GDALMDArray::GetMask().
   12552             :  */
   12553          35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
   12554             : {
   12555          35 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12556          70 :     auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
   12557          35 :     if (!unscaled)
   12558           7 :         return nullptr;
   12559          28 :     return new GDALMDArrayHS(unscaled);
   12560             : }
   12561             : 
   12562             : /************************************************************************/
   12563             : /*                   GDALMDArrayGetResampled()                          */
   12564             : /************************************************************************/
   12565             : 
   12566             : /** Return an array that is a resampled / reprojected view of the current array
   12567             :  *
   12568             :  * This is the same as the C++ method GDALMDArray::GetResampled().
   12569             :  *
   12570             :  * Currently this method can only resample along the last 2 dimensions, unless
   12571             :  * orthorectifying a NASA EMIT dataset.
   12572             :  *
   12573             :  * The returned object should be released with GDALMDArrayRelease().
   12574             :  *
   12575             :  * @since 3.4
   12576             :  */
   12577          34 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
   12578             :                                      const GDALDimensionH *pahNewDims,
   12579             :                                      GDALRIOResampleAlg resampleAlg,
   12580             :                                      OGRSpatialReferenceH hTargetSRS,
   12581             :                                      CSLConstList papszOptions)
   12582             : {
   12583          34 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12584          34 :     VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
   12585          68 :     std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
   12586         112 :     for (size_t i = 0; i < nNewDimCount; ++i)
   12587             :     {
   12588          78 :         if (pahNewDims[i])
   12589           8 :             apoNewDims[i] = pahNewDims[i]->m_poImpl;
   12590             :     }
   12591          34 :     auto poNewArray = hArray->m_poImpl->GetResampled(
   12592          34 :         apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
   12593          68 :         papszOptions);
   12594          34 :     if (!poNewArray)
   12595           8 :         return nullptr;
   12596          26 :     return new GDALMDArrayHS(poNewArray);
   12597             : }
   12598             : 
   12599             : /************************************************************************/
   12600             : /*                      GDALMDArraySetUnit()                            */
   12601             : /************************************************************************/
   12602             : 
   12603             : /** Set the variable unit.
   12604             :  *
   12605             :  * Values should conform as much as possible with those allowed by
   12606             :  * the NetCDF CF conventions:
   12607             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
   12608             :  * but others might be returned.
   12609             :  *
   12610             :  * Few examples are "meter", "degrees", "second", ...
   12611             :  * Empty value means unknown.
   12612             :  *
   12613             :  * This is the same as the C function GDALMDArraySetUnit()
   12614             :  *
   12615             :  * @param hArray array.
   12616             :  * @param pszUnit unit name.
   12617             :  * @return TRUE in case of success.
   12618             :  */
   12619          15 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
   12620             : {
   12621          15 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12622          15 :     return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
   12623             : }
   12624             : 
   12625             : /************************************************************************/
   12626             : /*                      GDALMDArrayGetUnit()                            */
   12627             : /************************************************************************/
   12628             : 
   12629             : /** Return the array unit.
   12630             :  *
   12631             :  * Values should conform as much as possible with those allowed by
   12632             :  * the NetCDF CF conventions:
   12633             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
   12634             :  * but others might be returned.
   12635             :  *
   12636             :  * Few examples are "meter", "degrees", "second", ...
   12637             :  * Empty value means unknown.
   12638             :  *
   12639             :  * The return value should not be freed and is valid until GDALMDArray is
   12640             :  * released or this function called again.
   12641             :  *
   12642             :  * This is the same as the C++ method GDALMDArray::GetUnit().
   12643             :  */
   12644         111 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
   12645             : {
   12646         111 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12647         111 :     return hArray->m_poImpl->GetUnit().c_str();
   12648             : }
   12649             : 
   12650             : /************************************************************************/
   12651             : /*                      GDALMDArrayGetSpatialRef()                      */
   12652             : /************************************************************************/
   12653             : 
   12654             : /** Assign a spatial reference system object to the array.
   12655             :  *
   12656             :  * This is the same as the C++ method GDALMDArray::SetSpatialRef().
   12657             :  * @return TRUE in case of success.
   12658             :  */
   12659          30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
   12660             : {
   12661          30 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12662          60 :     return hArray->m_poImpl->SetSpatialRef(
   12663          60 :         OGRSpatialReference::FromHandle(hSRS));
   12664             : }
   12665             : 
   12666             : /************************************************************************/
   12667             : /*                      GDALMDArrayGetSpatialRef()                      */
   12668             : /************************************************************************/
   12669             : 
   12670             : /** Return the spatial reference system object associated with the array.
   12671             :  *
   12672             :  * This is the same as the C++ method GDALMDArray::GetSpatialRef().
   12673             :  *
   12674             :  * The returned object must be freed with OSRDestroySpatialReference().
   12675             :  */
   12676          77 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
   12677             : {
   12678          77 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12679          77 :     auto poSRS = hArray->m_poImpl->GetSpatialRef();
   12680          77 :     return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
   12681             : }
   12682             : 
   12683             : /************************************************************************/
   12684             : /*                      GDALMDArrayGetStatistics()                      */
   12685             : /************************************************************************/
   12686             : 
   12687             : /**
   12688             :  * \brief Fetch statistics.
   12689             :  *
   12690             :  * This is the same as the C++ method GDALMDArray::GetStatistics().
   12691             :  *
   12692             :  * @since GDAL 3.2
   12693             :  */
   12694             : 
   12695          15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
   12696             :                                 int bApproxOK, int bForce, double *pdfMin,
   12697             :                                 double *pdfMax, double *pdfMean,
   12698             :                                 double *pdfStdDev, GUInt64 *pnValidCount,
   12699             :                                 GDALProgressFunc pfnProgress,
   12700             :                                 void *pProgressData)
   12701             : {
   12702          15 :     VALIDATE_POINTER1(hArray, __func__, CE_Failure);
   12703          30 :     return hArray->m_poImpl->GetStatistics(
   12704          15 :         CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
   12705          15 :         pdfStdDev, pnValidCount, pfnProgress, pProgressData);
   12706             : }
   12707             : 
   12708             : /************************************************************************/
   12709             : /*                      GDALMDArrayComputeStatistics()                  */
   12710             : /************************************************************************/
   12711             : 
   12712             : /**
   12713             :  * \brief Compute statistics.
   12714             :  *
   12715             :  * This is the same as the C++ method GDALMDArray::ComputeStatistics().
   12716             :  *
   12717             :  * @since GDAL 3.2
   12718             :  * @see GDALMDArrayComputeStatisticsEx()
   12719             :  */
   12720             : 
   12721           0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
   12722             :                                  int bApproxOK, double *pdfMin, double *pdfMax,
   12723             :                                  double *pdfMean, double *pdfStdDev,
   12724             :                                  GUInt64 *pnValidCount,
   12725             :                                  GDALProgressFunc pfnProgress,
   12726             :                                  void *pProgressData)
   12727             : {
   12728           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12729           0 :     return hArray->m_poImpl->ComputeStatistics(
   12730           0 :         CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
   12731           0 :         pnValidCount, pfnProgress, pProgressData, nullptr);
   12732             : }
   12733             : 
   12734             : /************************************************************************/
   12735             : /*                     GDALMDArrayComputeStatisticsEx()                 */
   12736             : /************************************************************************/
   12737             : 
   12738             : /**
   12739             :  * \brief Compute statistics.
   12740             :  *
   12741             :  * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
   12742             :  *
   12743             :  * This is the same as the C++ method GDALMDArray::ComputeStatistics().
   12744             :  *
   12745             :  * @since GDAL 3.8
   12746             :  */
   12747             : 
   12748           4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
   12749             :                                    int bApproxOK, double *pdfMin,
   12750             :                                    double *pdfMax, double *pdfMean,
   12751             :                                    double *pdfStdDev, GUInt64 *pnValidCount,
   12752             :                                    GDALProgressFunc pfnProgress,
   12753             :                                    void *pProgressData,
   12754             :                                    CSLConstList papszOptions)
   12755             : {
   12756           4 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12757           8 :     return hArray->m_poImpl->ComputeStatistics(
   12758           4 :         CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
   12759           8 :         pnValidCount, pfnProgress, pProgressData, papszOptions);
   12760             : }
   12761             : 
   12762             : /************************************************************************/
   12763             : /*                 GDALMDArrayGetCoordinateVariables()                  */
   12764             : /************************************************************************/
   12765             : 
   12766             : /** Return coordinate variables.
   12767             :  *
   12768             :  * The returned array must be freed with GDALReleaseArrays(). If only the array
   12769             :  * itself needs to be freed, CPLFree() should be called (and
   12770             :  * GDALMDArrayRelease() on individual array members).
   12771             :  *
   12772             :  * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
   12773             :  *
   12774             :  * @param hArray Array.
   12775             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12776             :  *
   12777             :  * @return an array of *pnCount arrays.
   12778             :  * @since 3.4
   12779             :  */
   12780          13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
   12781             :                                                 size_t *pnCount)
   12782             : {
   12783          13 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12784          13 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12785          13 :     const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
   12786             :     auto ret = static_cast<GDALMDArrayH *>(
   12787          13 :         CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
   12788          29 :     for (size_t i = 0; i < coordinates.size(); i++)
   12789             :     {
   12790          16 :         ret[i] = new GDALMDArrayHS(coordinates[i]);
   12791             :     }
   12792          13 :     *pnCount = coordinates.size();
   12793          13 :     return ret;
   12794             : }
   12795             : 
   12796             : /************************************************************************/
   12797             : /*                     GDALMDArrayGetGridded()                          */
   12798             : /************************************************************************/
   12799             : 
   12800             : /** Return a gridded array from scattered point data, that is from an array
   12801             :  * whose last dimension is the indexing variable of X and Y arrays.
   12802             :  *
   12803             :  * The returned object should be released with GDALMDArrayRelease().
   12804             :  *
   12805             :  * This is the same as the C++ method GDALMDArray::GetGridded().
   12806             :  *
   12807             :  * @since GDAL 3.7
   12808             :  */
   12809          22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
   12810             :                                    const char *pszGridOptions,
   12811             :                                    GDALMDArrayH hXArray, GDALMDArrayH hYArray,
   12812             :                                    CSLConstList papszOptions)
   12813             : {
   12814          22 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12815          22 :     VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
   12816          22 :     auto gridded = hArray->m_poImpl->GetGridded(
   12817          44 :         pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
   12818          88 :         hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
   12819          22 :     if (!gridded)
   12820          19 :         return nullptr;
   12821           3 :     return new GDALMDArrayHS(gridded);
   12822             : }
   12823             : 
   12824             : /************************************************************************/
   12825             : /*                      GDALMDArrayGetMeshGrid()                        */
   12826             : /************************************************************************/
   12827             : 
   12828             : /** Return a list of multidimensional arrays from a list of one-dimensional
   12829             :  * arrays.
   12830             :  *
   12831             :  * This is typically used to transform one-dimensional longitude, latitude
   12832             :  * arrays into 2D ones.
   12833             :  *
   12834             :  * More formally, for one-dimensional arrays x1, x2,..., xn with lengths
   12835             :  * Ni=len(xi), returns (N1, N2, ..., Nn) shaped arrays if indexing="ij" or
   12836             :  * (N2, N1, ..., Nn) shaped arrays if indexing="xy" with the elements of xi
   12837             :  * repeated to fill the matrix along the first dimension for x1, the second
   12838             :  * for x2 and so on.
   12839             :  *
   12840             :  * For example, if x = [1, 2], and y = [3, 4, 5],
   12841             :  * GetMeshGrid([x, y], ["INDEXING=xy"]) will return [xm, ym] such that
   12842             :  * xm=[[1, 2],[1, 2],[1, 2]] and ym=[[3, 3],[4, 4],[5, 5]],
   12843             :  * or more generally xm[any index][i] = x[i] and ym[i][any index]=y[i]
   12844             :  *
   12845             :  * and
   12846             :  * GetMeshGrid([x, y], ["INDEXING=ij"]) will return [xm, ym] such that
   12847             :  * xm=[[1, 1, 1],[2, 2, 2]] and ym=[[3, 4, 5],[3, 4, 5]],
   12848             :  * or more generally xm[i][any index] = x[i] and ym[any index][i]=y[i]
   12849             :  *
   12850             :  * The currently supported options are:
   12851             :  * <ul>
   12852             :  * <li>INDEXING=xy/ij: Cartesian ("xy", default) or matrix ("ij") indexing of
   12853             :  * output.
   12854             :  * </li>
   12855             :  * </ul>
   12856             :  *
   12857             :  * This is the same as
   12858             :  * <a href="https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html">numpy.meshgrid()</a>
   12859             :  * function.
   12860             :  *
   12861             :  * The returned array (of arrays) must be freed with GDALReleaseArrays().
   12862             :  * If only the array itself needs to be freed, CPLFree() should be called
   12863             :  * (and GDALMDArrayRelease() on individual array members).
   12864             :  *
   12865             :  * This is the same as the C++ method GDALMDArray::GetMeshGrid()
   12866             :  *
   12867             :  * @param pahInputArrays Input arrays
   12868             :  * @param nCountInputArrays Number of input arrays
   12869             :  * @param pnCountOutputArrays Pointer to the number of values returned. Must NOT be NULL.
   12870             :  * @param papszOptions NULL, or NULL terminated list of options.
   12871             :  *
   12872             :  * @return an array of *pnCountOutputArrays arrays.
   12873             :  * @since 3.10
   12874             :  */
   12875           7 : GDALMDArrayH *GDALMDArrayGetMeshGrid(const GDALMDArrayH *pahInputArrays,
   12876             :                                      size_t nCountInputArrays,
   12877             :                                      size_t *pnCountOutputArrays,
   12878             :                                      CSLConstList papszOptions)
   12879             : {
   12880           7 :     VALIDATE_POINTER1(pahInputArrays, __func__, nullptr);
   12881           7 :     VALIDATE_POINTER1(pnCountOutputArrays, __func__, nullptr);
   12882             : 
   12883          14 :     std::vector<std::shared_ptr<GDALMDArray>> apoInputArrays;
   12884          20 :     for (size_t i = 0; i < nCountInputArrays; ++i)
   12885          13 :         apoInputArrays.push_back(pahInputArrays[i]->m_poImpl);
   12886             : 
   12887             :     const auto apoOutputArrays =
   12888           7 :         GDALMDArray::GetMeshGrid(apoInputArrays, papszOptions);
   12889             :     auto ret = static_cast<GDALMDArrayH *>(
   12890           7 :         CPLMalloc(sizeof(GDALMDArrayH) * apoOutputArrays.size()));
   12891          17 :     for (size_t i = 0; i < apoOutputArrays.size(); i++)
   12892             :     {
   12893          10 :         ret[i] = new GDALMDArrayHS(apoOutputArrays[i]);
   12894             :     }
   12895           7 :     *pnCountOutputArrays = apoOutputArrays.size();
   12896           7 :     return ret;
   12897             : }
   12898             : 
   12899             : /************************************************************************/
   12900             : /*                        GDALReleaseArrays()                           */
   12901             : /************************************************************************/
   12902             : 
   12903             : /** Free the return of GDALMDArrayGetCoordinateVariables()
   12904             :  *
   12905             :  * @param arrays return pointer of above methods
   12906             :  * @param nCount *pnCount value returned by above methods
   12907             :  */
   12908          20 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
   12909             : {
   12910          46 :     for (size_t i = 0; i < nCount; i++)
   12911             :     {
   12912          26 :         delete arrays[i];
   12913             :     }
   12914          20 :     CPLFree(arrays);
   12915          20 : }
   12916             : 
   12917             : /************************************************************************/
   12918             : /*                           GDALMDArrayCache()                         */
   12919             : /************************************************************************/
   12920             : 
   12921             : /**
   12922             :  * \brief Cache the content of the array into an auxiliary filename.
   12923             :  *
   12924             :  * This is the same as the C++ method GDALMDArray::Cache().
   12925             :  *
   12926             :  * @since GDAL 3.4
   12927             :  */
   12928             : 
   12929           7 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
   12930             : {
   12931           7 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12932           7 :     return hArray->m_poImpl->Cache(papszOptions);
   12933             : }
   12934             : 
   12935             : /************************************************************************/
   12936             : /*                       GDALMDArrayRename()                           */
   12937             : /************************************************************************/
   12938             : 
   12939             : /** Rename the array.
   12940             :  *
   12941             :  * This is not implemented by all drivers.
   12942             :  *
   12943             :  * Drivers known to implement it: MEM, netCDF, Zarr.
   12944             :  *
   12945             :  * This is the same as the C++ method GDALAbstractMDArray::Rename()
   12946             :  *
   12947             :  * @return true in case of success
   12948             :  * @since GDAL 3.8
   12949             :  */
   12950          28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
   12951             : {
   12952          28 :     VALIDATE_POINTER1(hArray, __func__, false);
   12953          28 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   12954          28 :     return hArray->m_poImpl->Rename(pszNewName);
   12955             : }
   12956             : 
   12957             : /************************************************************************/
   12958             : /*                        GDALAttributeRelease()                        */
   12959             : /************************************************************************/
   12960             : 
   12961             : /** Release the GDAL in-memory object associated with a GDALAttribute.
   12962             :  *
   12963             :  * Note: when applied on a object coming from a driver, this does not
   12964             :  * destroy the object in the file, database, etc...
   12965             :  */
   12966         714 : void GDALAttributeRelease(GDALAttributeH hAttr)
   12967             : {
   12968         714 :     delete hAttr;
   12969         714 : }
   12970             : 
   12971             : /************************************************************************/
   12972             : /*                        GDALAttributeGetName()                        */
   12973             : /************************************************************************/
   12974             : 
   12975             : /** Return the name of the attribute.
   12976             :  *
   12977             :  * The returned pointer is valid until hAttr is released.
   12978             :  *
   12979             :  * This is the same as the C++ method GDALAttribute::GetName().
   12980             :  */
   12981         361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
   12982             : {
   12983         361 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   12984         361 :     return hAttr->m_poImpl->GetName().c_str();
   12985             : }
   12986             : 
   12987             : /************************************************************************/
   12988             : /*                      GDALAttributeGetFullName()                      */
   12989             : /************************************************************************/
   12990             : 
   12991             : /** Return the full name of the attribute.
   12992             :  *
   12993             :  * The returned pointer is valid until hAttr is released.
   12994             :  *
   12995             :  * This is the same as the C++ method GDALAttribute::GetFullName().
   12996             :  */
   12997          49 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
   12998             : {
   12999          49 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13000          49 :     return hAttr->m_poImpl->GetFullName().c_str();
   13001             : }
   13002             : 
   13003             : /************************************************************************/
   13004             : /*                   GDALAttributeGetTotalElementsCount()               */
   13005             : /************************************************************************/
   13006             : 
   13007             : /** Return the total number of values in the attribute.
   13008             :  *
   13009             :  * This is the same as the C++ method
   13010             :  * GDALAbstractMDArray::GetTotalElementsCount()
   13011             :  */
   13012         176 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
   13013             : {
   13014         176 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13015         176 :     return hAttr->m_poImpl->GetTotalElementsCount();
   13016             : }
   13017             : 
   13018             : /************************************************************************/
   13019             : /*                    GDALAttributeGetDimensionCount()                */
   13020             : /************************************************************************/
   13021             : 
   13022             : /** Return the number of dimensions.
   13023             :  *
   13024             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
   13025             :  */
   13026          12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
   13027             : {
   13028          12 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13029          12 :     return hAttr->m_poImpl->GetDimensionCount();
   13030             : }
   13031             : 
   13032             : /************************************************************************/
   13033             : /*                       GDALAttributeGetDimensionsSize()                */
   13034             : /************************************************************************/
   13035             : 
   13036             : /** Return the dimension sizes of the attribute.
   13037             :  *
   13038             :  * The returned array must be freed with CPLFree()
   13039             :  *
   13040             :  * @param hAttr Attribute.
   13041             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13042             :  *
   13043             :  * @return an array of *pnCount values.
   13044             :  */
   13045          11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
   13046             : {
   13047          11 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13048          11 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13049          11 :     const auto &dims = hAttr->m_poImpl->GetDimensions();
   13050          11 :     auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
   13051          22 :     for (size_t i = 0; i < dims.size(); i++)
   13052             :     {
   13053          11 :         ret[i] = dims[i]->GetSize();
   13054             :     }
   13055          11 :     *pnCount = dims.size();
   13056          11 :     return ret;
   13057             : }
   13058             : 
   13059             : /************************************************************************/
   13060             : /*                       GDALAttributeGetDataType()                     */
   13061             : /************************************************************************/
   13062             : 
   13063             : /** Return the data type
   13064             :  *
   13065             :  * The return must be freed with GDALExtendedDataTypeRelease().
   13066             :  */
   13067         427 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
   13068             : {
   13069         427 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13070             :     return new GDALExtendedDataTypeHS(
   13071         427 :         new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
   13072             : }
   13073             : 
   13074             : /************************************************************************/
   13075             : /*                       GDALAttributeReadAsRaw()                       */
   13076             : /************************************************************************/
   13077             : 
   13078             : /** Return the raw value of an attribute.
   13079             :  *
   13080             :  * This is the same as the C++ method GDALAttribute::ReadAsRaw().
   13081             :  *
   13082             :  * The returned buffer must be freed with GDALAttributeFreeRawResult()
   13083             :  *
   13084             :  * @param hAttr Attribute.
   13085             :  * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
   13086             :  *
   13087             :  * @return a buffer of *pnSize bytes.
   13088             :  */
   13089           6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
   13090             : {
   13091           6 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13092           6 :     VALIDATE_POINTER1(pnSize, __func__, nullptr);
   13093          12 :     auto res(hAttr->m_poImpl->ReadAsRaw());
   13094           6 :     *pnSize = res.size();
   13095           6 :     auto ret = res.StealData();
   13096           6 :     if (!ret)
   13097             :     {
   13098           0 :         *pnSize = 0;
   13099           0 :         return nullptr;
   13100             :     }
   13101           6 :     return ret;
   13102             : }
   13103             : 
   13104             : /************************************************************************/
   13105             : /*                       GDALAttributeFreeRawResult()                   */
   13106             : /************************************************************************/
   13107             : 
   13108             : /** Free the return of GDALAttributeAsRaw()
   13109             :  */
   13110           6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
   13111             :                                 CPL_UNUSED size_t nSize)
   13112             : {
   13113           6 :     VALIDATE_POINTER0(hAttr, __func__);
   13114           6 :     if (raw)
   13115             :     {
   13116           6 :         const auto &dt(hAttr->m_poImpl->GetDataType());
   13117           6 :         const auto nDTSize(dt.GetSize());
   13118           6 :         GByte *pabyPtr = raw;
   13119           6 :         const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
   13120           6 :         CPLAssert(nSize == nDTSize * nEltCount);
   13121          12 :         for (size_t i = 0; i < nEltCount; ++i)
   13122             :         {
   13123           6 :             dt.FreeDynamicMemory(pabyPtr);
   13124           6 :             pabyPtr += nDTSize;
   13125             :         }
   13126           6 :         CPLFree(raw);
   13127             :     }
   13128             : }
   13129             : 
   13130             : /************************************************************************/
   13131             : /*                       GDALAttributeReadAsString()                    */
   13132             : /************************************************************************/
   13133             : 
   13134             : /** Return the value of an attribute as a string.
   13135             :  *
   13136             :  * The returned string should not be freed, and its lifetime does not
   13137             :  * excess a next call to ReadAsString() on the same object, or the deletion
   13138             :  * of the object itself.
   13139             :  *
   13140             :  * This function will only return the first element if there are several.
   13141             :  *
   13142             :  * This is the same as the C++ method GDALAttribute::ReadAsString()
   13143             :  *
   13144             :  * @return a string, or nullptr.
   13145             :  */
   13146         107 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
   13147             : {
   13148         107 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13149         107 :     return hAttr->m_poImpl->ReadAsString();
   13150             : }
   13151             : 
   13152             : /************************************************************************/
   13153             : /*                      GDALAttributeReadAsInt()                        */
   13154             : /************************************************************************/
   13155             : 
   13156             : /** Return the value of an attribute as a integer.
   13157             :  *
   13158             :  * This function will only return the first element if there are several.
   13159             :  *
   13160             :  * It can fail if its value can not be converted to integer.
   13161             :  *
   13162             :  * This is the same as the C++ method GDALAttribute::ReadAsInt()
   13163             :  *
   13164             :  * @return a integer, or INT_MIN in case of error.
   13165             :  */
   13166          22 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
   13167             : {
   13168          22 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13169          22 :     return hAttr->m_poImpl->ReadAsInt();
   13170             : }
   13171             : 
   13172             : /************************************************************************/
   13173             : /*                      GDALAttributeReadAsInt64()                      */
   13174             : /************************************************************************/
   13175             : 
   13176             : /** Return the value of an attribute as a int64_t.
   13177             :  *
   13178             :  * This function will only return the first element if there are several.
   13179             :  *
   13180             :  * It can fail if its value can not be converted to integer.
   13181             :  *
   13182             :  * This is the same as the C++ method GDALAttribute::ReadAsInt64()
   13183             :  *
   13184             :  * @return an int64_t, or INT64_MIN in case of error.
   13185             :  */
   13186          15 : int64_t GDALAttributeReadAsInt64(GDALAttributeH hAttr)
   13187             : {
   13188          15 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13189          15 :     return hAttr->m_poImpl->ReadAsInt64();
   13190             : }
   13191             : 
   13192             : /************************************************************************/
   13193             : /*                       GDALAttributeReadAsDouble()                    */
   13194             : /************************************************************************/
   13195             : 
   13196             : /** Return the value of an attribute as a double.
   13197             :  *
   13198             :  * This function will only return the first element if there are several.
   13199             :  *
   13200             :  * It can fail if its value can not be converted to double.
   13201             :  *
   13202             :  * This is the same as the C++ method GDALAttribute::ReadAsDouble()
   13203             :  *
   13204             :  * @return a double value.
   13205             :  */
   13206          40 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
   13207             : {
   13208          40 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   13209          40 :     return hAttr->m_poImpl->ReadAsDouble();
   13210             : }
   13211             : 
   13212             : /************************************************************************/
   13213             : /*                     GDALAttributeReadAsStringArray()                 */
   13214             : /************************************************************************/
   13215             : 
   13216             : /** Return the value of an attribute as an array of strings.
   13217             :  *
   13218             :  * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
   13219             :  *
   13220             :  * The return value must be freed with CSLDestroy().
   13221             :  */
   13222          19 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
   13223             : {
   13224          19 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13225          19 :     return hAttr->m_poImpl->ReadAsStringArray().StealList();
   13226             : }
   13227             : 
   13228             : /************************************************************************/
   13229             : /*                     GDALAttributeReadAsIntArray()                    */
   13230             : /************************************************************************/
   13231             : 
   13232             : /** Return the value of an attribute as an array of integers.
   13233             :  *
   13234             :  * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
   13235             :  *
   13236             :  * @param hAttr Attribute
   13237             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13238             :  * @return array to be freed with CPLFree(), or nullptr.
   13239             :  */
   13240          15 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
   13241             : {
   13242          15 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13243          15 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13244          15 :     *pnCount = 0;
   13245          30 :     auto tmp(hAttr->m_poImpl->ReadAsIntArray());
   13246          15 :     if (tmp.empty())
   13247           0 :         return nullptr;
   13248          15 :     auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
   13249          15 :     if (!ret)
   13250           0 :         return nullptr;
   13251          15 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
   13252          15 :     *pnCount = tmp.size();
   13253          15 :     return ret;
   13254             : }
   13255             : 
   13256             : /************************************************************************/
   13257             : /*                     GDALAttributeReadAsInt64Array()                  */
   13258             : /************************************************************************/
   13259             : 
   13260             : /** Return the value of an attribute as an array of int64_t.
   13261             :  *
   13262             :  * This is the same as the C++ method GDALAttribute::ReadAsInt64Array()
   13263             :  *
   13264             :  * @param hAttr Attribute
   13265             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13266             :  * @return array to be freed with CPLFree(), or nullptr.
   13267             :  */
   13268          14 : int64_t *GDALAttributeReadAsInt64Array(GDALAttributeH hAttr, size_t *pnCount)
   13269             : {
   13270          14 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13271          14 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13272          14 :     *pnCount = 0;
   13273          28 :     auto tmp(hAttr->m_poImpl->ReadAsInt64Array());
   13274          14 :     if (tmp.empty())
   13275           0 :         return nullptr;
   13276             :     auto ret = static_cast<int64_t *>(
   13277          14 :         VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int64_t)));
   13278          14 :     if (!ret)
   13279           0 :         return nullptr;
   13280          14 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(int64_t));
   13281          14 :     *pnCount = tmp.size();
   13282          14 :     return ret;
   13283             : }
   13284             : 
   13285             : /************************************************************************/
   13286             : /*                     GDALAttributeReadAsDoubleArray()                 */
   13287             : /************************************************************************/
   13288             : 
   13289             : /** Return the value of an attribute as an array of doubles.
   13290             :  *
   13291             :  * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
   13292             :  *
   13293             :  * @param hAttr Attribute
   13294             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   13295             :  * @return array to be freed with CPLFree(), or nullptr.
   13296             :  */
   13297          29 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
   13298             : {
   13299          29 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   13300          29 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   13301          29 :     *pnCount = 0;
   13302          58 :     auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
   13303          29 :     if (tmp.empty())
   13304           0 :         return nullptr;
   13305             :     auto ret =
   13306          29 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
   13307          29 :     if (!ret)
   13308           0 :         return nullptr;
   13309          29 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
   13310          29 :     *pnCount = tmp.size();
   13311          29 :     return ret;
   13312             : }
   13313             : 
   13314             : /************************************************************************/
   13315             : /*                     GDALAttributeWriteRaw()                          */
   13316             : /************************************************************************/
   13317             : 
   13318             : /** Write an attribute from raw values expressed in GetDataType()
   13319             :  *
   13320             :  * The values should be provided in the type of GetDataType() and there should
   13321             :  * be exactly GetTotalElementsCount() of them.
   13322             :  * If GetDataType() is a string, each value should be a char* pointer.
   13323             :  *
   13324             :  * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
   13325             :  *
   13326             :  * @param hAttr Attribute
   13327             :  * @param pabyValue Buffer of nLen bytes.
   13328             :  * @param nLength Size of pabyValue in bytes. Should be equal to
   13329             :  *             GetTotalElementsCount() * GetDataType().GetSize()
   13330             :  * @return TRUE in case of success.
   13331             :  */
   13332           5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
   13333             :                           size_t nLength)
   13334             : {
   13335           5 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13336           5 :     return hAttr->m_poImpl->Write(pabyValue, nLength);
   13337             : }
   13338             : 
   13339             : /************************************************************************/
   13340             : /*                     GDALAttributeWriteString()                       */
   13341             : /************************************************************************/
   13342             : 
   13343             : /** Write an attribute from a string value.
   13344             :  *
   13345             :  * Type conversion will be performed if needed. If the attribute contains
   13346             :  * multiple values, only the first one will be updated.
   13347             :  *
   13348             :  * This is the same as the C++ method GDALAttribute::Write(const char*)
   13349             :  *
   13350             :  * @param hAttr Attribute
   13351             :  * @param pszVal Pointer to a string.
   13352             :  * @return TRUE in case of success.
   13353             :  */
   13354         175 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
   13355             : {
   13356         175 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13357         175 :     return hAttr->m_poImpl->Write(pszVal);
   13358             : }
   13359             : 
   13360             : /************************************************************************/
   13361             : /*                        GDALAttributeWriteInt()                       */
   13362             : /************************************************************************/
   13363             : 
   13364             : /** Write an attribute from a integer value.
   13365             :  *
   13366             :  * Type conversion will be performed if needed. If the attribute contains
   13367             :  * multiple values, only the first one will be updated.
   13368             :  *
   13369             :  * This is the same as the C++ method GDALAttribute::WriteInt()
   13370             :  *
   13371             :  * @param hAttr Attribute
   13372             :  * @param nVal Value.
   13373             :  * @return TRUE in case of success.
   13374             :  */
   13375          22 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
   13376             : {
   13377          22 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13378          22 :     return hAttr->m_poImpl->WriteInt(nVal);
   13379             : }
   13380             : 
   13381             : /************************************************************************/
   13382             : /*                        GDALAttributeWriteInt64()                     */
   13383             : /************************************************************************/
   13384             : 
   13385             : /** Write an attribute from an int64_t value.
   13386             :  *
   13387             :  * Type conversion will be performed if needed. If the attribute contains
   13388             :  * multiple values, only the first one will be updated.
   13389             :  *
   13390             :  * This is the same as the C++ method GDALAttribute::WriteLong()
   13391             :  *
   13392             :  * @param hAttr Attribute
   13393             :  * @param nVal Value.
   13394             :  * @return TRUE in case of success.
   13395             :  */
   13396          11 : int GDALAttributeWriteInt64(GDALAttributeH hAttr, int64_t nVal)
   13397             : {
   13398          11 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13399          11 :     return hAttr->m_poImpl->WriteInt64(nVal);
   13400             : }
   13401             : 
   13402             : /************************************************************************/
   13403             : /*                        GDALAttributeWriteDouble()                    */
   13404             : /************************************************************************/
   13405             : 
   13406             : /** Write an attribute from a double value.
   13407             :  *
   13408             :  * Type conversion will be performed if needed. If the attribute contains
   13409             :  * multiple values, only the first one will be updated.
   13410             :  *
   13411             :  * This is the same as the C++ method GDALAttribute::Write(double);
   13412             :  *
   13413             :  * @param hAttr Attribute
   13414             :  * @param dfVal Value.
   13415             :  *
   13416             :  * @return TRUE in case of success.
   13417             :  */
   13418          11 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
   13419             : {
   13420          11 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13421          11 :     return hAttr->m_poImpl->Write(dfVal);
   13422             : }
   13423             : 
   13424             : /************************************************************************/
   13425             : /*                       GDALAttributeWriteStringArray()                */
   13426             : /************************************************************************/
   13427             : 
   13428             : /** Write an attribute from an array of strings.
   13429             :  *
   13430             :  * Type conversion will be performed if needed.
   13431             :  *
   13432             :  * Exactly GetTotalElementsCount() strings must be provided
   13433             :  *
   13434             :  * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
   13435             :  *
   13436             :  * @param hAttr Attribute
   13437             :  * @param papszValues Array of strings.
   13438             :  * @return TRUE in case of success.
   13439             :  */
   13440           8 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
   13441             :                                   CSLConstList papszValues)
   13442             : {
   13443           8 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13444           8 :     return hAttr->m_poImpl->Write(papszValues);
   13445             : }
   13446             : 
   13447             : /************************************************************************/
   13448             : /*                       GDALAttributeWriteIntArray()                */
   13449             : /************************************************************************/
   13450             : 
   13451             : /** Write an attribute from an array of int.
   13452             :  *
   13453             :  * Type conversion will be performed if needed.
   13454             :  *
   13455             :  * Exactly GetTotalElementsCount() strings must be provided
   13456             :  *
   13457             :  * This is the same as the C++ method GDALAttribute::Write(const int *,
   13458             :  * size_t)
   13459             :  *
   13460             :  * @param hAttr Attribute
   13461             :  * @param panValues Array of int.
   13462             :  * @param nCount Should be equal to GetTotalElementsCount().
   13463             :  * @return TRUE in case of success.
   13464             :  */
   13465           9 : int GDALAttributeWriteIntArray(GDALAttributeH hAttr, const int *panValues,
   13466             :                                size_t nCount)
   13467             : {
   13468           9 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13469           9 :     return hAttr->m_poImpl->Write(panValues, nCount);
   13470             : }
   13471             : 
   13472             : /************************************************************************/
   13473             : /*                       GDALAttributeWriteInt64Array()                 */
   13474             : /************************************************************************/
   13475             : 
   13476             : /** Write an attribute from an array of int64_t.
   13477             :  *
   13478             :  * Type conversion will be performed if needed.
   13479             :  *
   13480             :  * Exactly GetTotalElementsCount() strings must be provided
   13481             :  *
   13482             :  * This is the same as the C++ method GDALAttribute::Write(const int64_t *,
   13483             :  * size_t)
   13484             :  *
   13485             :  * @param hAttr Attribute
   13486             :  * @param panValues Array of int64_t.
   13487             :  * @param nCount Should be equal to GetTotalElementsCount().
   13488             :  * @return TRUE in case of success.
   13489             :  */
   13490          10 : int GDALAttributeWriteInt64Array(GDALAttributeH hAttr, const int64_t *panValues,
   13491             :                                  size_t nCount)
   13492             : {
   13493          10 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13494          10 :     return hAttr->m_poImpl->Write(panValues, nCount);
   13495             : }
   13496             : 
   13497             : /************************************************************************/
   13498             : /*                       GDALAttributeWriteDoubleArray()                */
   13499             : /************************************************************************/
   13500             : 
   13501             : /** Write an attribute from an array of double.
   13502             :  *
   13503             :  * Type conversion will be performed if needed.
   13504             :  *
   13505             :  * Exactly GetTotalElementsCount() strings must be provided
   13506             :  *
   13507             :  * This is the same as the C++ method GDALAttribute::Write(const double *,
   13508             :  * size_t)
   13509             :  *
   13510             :  * @param hAttr Attribute
   13511             :  * @param padfValues Array of double.
   13512             :  * @param nCount Should be equal to GetTotalElementsCount().
   13513             :  * @return TRUE in case of success.
   13514             :  */
   13515           7 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
   13516             :                                   const double *padfValues, size_t nCount)
   13517             : {
   13518           7 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   13519           7 :     return hAttr->m_poImpl->Write(padfValues, nCount);
   13520             : }
   13521             : 
   13522             : /************************************************************************/
   13523             : /*                      GDALAttributeRename()                           */
   13524             : /************************************************************************/
   13525             : 
   13526             : /** Rename the attribute.
   13527             :  *
   13528             :  * This is not implemented by all drivers.
   13529             :  *
   13530             :  * Drivers known to implement it: MEM, netCDF.
   13531             :  *
   13532             :  * This is the same as the C++ method GDALAbstractMDArray::Rename()
   13533             :  *
   13534             :  * @return true in case of success
   13535             :  * @since GDAL 3.8
   13536             :  */
   13537          27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
   13538             : {
   13539          27 :     VALIDATE_POINTER1(hAttr, __func__, false);
   13540          27 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   13541          27 :     return hAttr->m_poImpl->Rename(pszNewName);
   13542             : }
   13543             : 
   13544             : /************************************************************************/
   13545             : /*                        GDALDimensionRelease()                        */
   13546             : /************************************************************************/
   13547             : 
   13548             : /** Release the GDAL in-memory object associated with a GDALDimension.
   13549             :  *
   13550             :  * Note: when applied on a object coming from a driver, this does not
   13551             :  * destroy the object in the file, database, etc...
   13552             :  */
   13553        4842 : void GDALDimensionRelease(GDALDimensionH hDim)
   13554             : {
   13555        4842 :     delete hDim;
   13556        4842 : }
   13557             : 
   13558             : /************************************************************************/
   13559             : /*                        GDALDimensionGetName()                        */
   13560             : /************************************************************************/
   13561             : 
   13562             : /** Return dimension name.
   13563             :  *
   13564             :  * This is the same as the C++ method GDALDimension::GetName()
   13565             :  */
   13566         284 : const char *GDALDimensionGetName(GDALDimensionH hDim)
   13567             : {
   13568         284 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13569         284 :     return hDim->m_poImpl->GetName().c_str();
   13570             : }
   13571             : 
   13572             : /************************************************************************/
   13573             : /*                      GDALDimensionGetFullName()                      */
   13574             : /************************************************************************/
   13575             : 
   13576             : /** Return dimension full name.
   13577             :  *
   13578             :  * This is the same as the C++ method GDALDimension::GetFullName()
   13579             :  */
   13580          80 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
   13581             : {
   13582          80 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13583          80 :     return hDim->m_poImpl->GetFullName().c_str();
   13584             : }
   13585             : 
   13586             : /************************************************************************/
   13587             : /*                        GDALDimensionGetType()                        */
   13588             : /************************************************************************/
   13589             : 
   13590             : /** Return dimension type.
   13591             :  *
   13592             :  * This is the same as the C++ method GDALDimension::GetType()
   13593             :  */
   13594          62 : const char *GDALDimensionGetType(GDALDimensionH hDim)
   13595             : {
   13596          62 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13597          62 :     return hDim->m_poImpl->GetType().c_str();
   13598             : }
   13599             : 
   13600             : /************************************************************************/
   13601             : /*                     GDALDimensionGetDirection()                      */
   13602             : /************************************************************************/
   13603             : 
   13604             : /** Return dimension direction.
   13605             :  *
   13606             :  * This is the same as the C++ method GDALDimension::GetDirection()
   13607             :  */
   13608          32 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
   13609             : {
   13610          32 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13611          32 :     return hDim->m_poImpl->GetDirection().c_str();
   13612             : }
   13613             : 
   13614             : /************************************************************************/
   13615             : /*                        GDALDimensionGetSize()                        */
   13616             : /************************************************************************/
   13617             : 
   13618             : /** Return the size, that is the number of values along the dimension.
   13619             :  *
   13620             :  * This is the same as the C++ method GDALDimension::GetSize()
   13621             :  */
   13622        3629 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
   13623             : {
   13624        3629 :     VALIDATE_POINTER1(hDim, __func__, 0);
   13625        3629 :     return hDim->m_poImpl->GetSize();
   13626             : }
   13627             : 
   13628             : /************************************************************************/
   13629             : /*                     GDALDimensionGetIndexingVariable()               */
   13630             : /************************************************************************/
   13631             : 
   13632             : /** Return the variable that is used to index the dimension (if there is one).
   13633             :  *
   13634             :  * This is the array, typically one-dimensional, describing the values taken
   13635             :  * by the dimension.
   13636             :  *
   13637             :  * The returned value should be freed with GDALMDArrayRelease().
   13638             :  *
   13639             :  * This is the same as the C++ method GDALDimension::GetIndexingVariable()
   13640             :  */
   13641         118 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
   13642             : {
   13643         118 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13644         236 :     auto var(hDim->m_poImpl->GetIndexingVariable());
   13645         118 :     if (!var)
   13646          10 :         return nullptr;
   13647         108 :     return new GDALMDArrayHS(var);
   13648             : }
   13649             : 
   13650             : /************************************************************************/
   13651             : /*                      GDALDimensionSetIndexingVariable()              */
   13652             : /************************************************************************/
   13653             : 
   13654             : /** Set the variable that is used to index the dimension.
   13655             :  *
   13656             :  * This is the array, typically one-dimensional, describing the values taken
   13657             :  * by the dimension.
   13658             :  *
   13659             :  * This is the same as the C++ method GDALDimension::SetIndexingVariable()
   13660             :  *
   13661             :  * @return TRUE in case of success.
   13662             :  */
   13663          23 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
   13664             : {
   13665          23 :     VALIDATE_POINTER1(hDim, __func__, FALSE);
   13666          69 :     return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
   13667          46 :                                                       : nullptr);
   13668             : }
   13669             : 
   13670             : /************************************************************************/
   13671             : /*                      GDALDimensionRename()                           */
   13672             : /************************************************************************/
   13673             : 
   13674             : /** Rename the dimension.
   13675             :  *
   13676             :  * This is not implemented by all drivers.
   13677             :  *
   13678             :  * Drivers known to implement it: MEM, netCDF.
   13679             :  *
   13680             :  * This is the same as the C++ method GDALDimension::Rename()
   13681             :  *
   13682             :  * @return true in case of success
   13683             :  * @since GDAL 3.8
   13684             :  */
   13685          31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
   13686             : {
   13687          31 :     VALIDATE_POINTER1(hDim, __func__, false);
   13688          31 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   13689          31 :     return hDim->m_poImpl->Rename(pszNewName);
   13690             : }
   13691             : 
   13692             : /************************************************************************/
   13693             : /*                       GDALDatasetGetRootGroup()                      */
   13694             : /************************************************************************/
   13695             : 
   13696             : /** Return the root GDALGroup of this dataset.
   13697             :  *
   13698             :  * Only valid for multidimensional datasets.
   13699             :  *
   13700             :  * The returned value must be freed with GDALGroupRelease().
   13701             :  *
   13702             :  * This is the same as the C++ method GDALDataset::GetRootGroup().
   13703             :  *
   13704             :  * @since GDAL 3.1
   13705             :  */
   13706        1129 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
   13707             : {
   13708        1129 :     VALIDATE_POINTER1(hDS, __func__, nullptr);
   13709        1129 :     auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
   13710        1129 :     return poGroup ? new GDALGroupHS(poGroup) : nullptr;
   13711             : }
   13712             : 
   13713             : /************************************************************************/
   13714             : /*                      GDALRasterBandAsMDArray()                        */
   13715             : /************************************************************************/
   13716             : 
   13717             : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
   13718             :  *
   13719             :  * The band must be linked to a GDALDataset. If this dataset is not already
   13720             :  * marked as shared, it will be, so that the returned array holds a reference
   13721             :  * to it.
   13722             :  *
   13723             :  * If the dataset has a geotransform attached, the X and Y dimensions of the
   13724             :  * returned array will have an associated indexing variable.
   13725             :  *
   13726             :  * The returned pointer must be released with GDALMDArrayRelease().
   13727             :  *
   13728             :  * This is the same as the C++ method GDALRasterBand::AsMDArray().
   13729             :  *
   13730             :  * @return a new array, or NULL.
   13731             :  *
   13732             :  * @since GDAL 3.1
   13733             :  */
   13734          21 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
   13735             : {
   13736          21 :     VALIDATE_POINTER1(hBand, __func__, nullptr);
   13737          42 :     auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
   13738          21 :     if (!poArray)
   13739           0 :         return nullptr;
   13740          21 :     return new GDALMDArrayHS(poArray);
   13741             : }
   13742             : 
   13743             : /************************************************************************/
   13744             : /*                       GDALMDArrayAsClassicDataset()                  */
   13745             : /************************************************************************/
   13746             : 
   13747             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   13748             :  *
   13749             :  * Only 2D or more arrays are supported.
   13750             :  *
   13751             :  * In the case of > 2D arrays, additional dimensions will be represented as
   13752             :  * raster bands.
   13753             :  *
   13754             :  * The "reverse" method is GDALRasterBand::AsMDArray().
   13755             :  *
   13756             :  * This is the same as the C++ method GDALMDArray::AsClassicDataset().
   13757             :  *
   13758             :  * @param hArray Array.
   13759             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   13760             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   13761             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   13762             :  */
   13763           0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
   13764             :                                          size_t iYDim)
   13765             : {
   13766           0 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13767           0 :     return GDALDataset::ToHandle(
   13768           0 :         hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
   13769             : }
   13770             : 
   13771             : /************************************************************************/
   13772             : /*                     GDALMDArrayAsClassicDatasetEx()                  */
   13773             : /************************************************************************/
   13774             : 
   13775             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   13776             :  *
   13777             :  * Only 2D or more arrays are supported.
   13778             :  *
   13779             :  * In the case of > 2D arrays, additional dimensions will be represented as
   13780             :  * raster bands.
   13781             :  *
   13782             :  * The "reverse" method is GDALRasterBand::AsMDArray().
   13783             :  *
   13784             :  * This is the same as the C++ method GDALMDArray::AsClassicDataset().
   13785             :  * @param hArray Array.
   13786             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   13787             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   13788             :  *              Ignored if the dimension count is 1.
   13789             :  * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA and
   13790             :  *                   BAND_IMAGERY_METADATA option.
   13791             :  * @param papszOptions Cf GDALMDArray::AsClassicDataset()
   13792             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   13793             :  * @since GDAL 3.8
   13794             :  */
   13795          71 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
   13796             :                                            size_t iYDim, GDALGroupH hRootGroup,
   13797             :                                            CSLConstList papszOptions)
   13798             : {
   13799          71 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13800         142 :     return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
   13801         142 :         iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
   13802         142 :         papszOptions));
   13803             : }
   13804             : 
   13805             : //! @cond Doxygen_Suppress
   13806             : 
   13807         180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
   13808             :                                          const std::string &osName,
   13809             :                                          const std::string &osValue,
   13810         180 :                                          GDALExtendedDataTypeSubType eSubType)
   13811             :     : GDALAbstractMDArray(osParentName, osName),
   13812             :       GDALAttribute(osParentName, osName),
   13813         180 :       m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
   13814             : {
   13815         180 : }
   13816             : 
   13817             : const std::vector<std::shared_ptr<GDALDimension>> &
   13818          30 : GDALAttributeString::GetDimensions() const
   13819             : {
   13820          30 :     return m_dims;
   13821             : }
   13822             : 
   13823          21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
   13824             : {
   13825          21 :     return m_dt;
   13826             : }
   13827             : 
   13828          10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
   13829             :                                 const GPtrDiff_t *,
   13830             :                                 const GDALExtendedDataType &bufferDataType,
   13831             :                                 void *pDstBuffer) const
   13832             : {
   13833          10 :     if (bufferDataType.GetClass() != GEDTC_STRING)
   13834           0 :         return false;
   13835          10 :     char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
   13836          10 :     if (!pszStr)
   13837           0 :         return false;
   13838          10 :     memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
   13839          10 :     *static_cast<char **>(pDstBuffer) = pszStr;
   13840          10 :     return true;
   13841             : }
   13842             : 
   13843          66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   13844             :                                            const std::string &osName,
   13845          66 :                                            double dfValue)
   13846             :     : GDALAbstractMDArray(osParentName, osName),
   13847             :       GDALAttribute(osParentName, osName),
   13848          66 :       m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
   13849             : {
   13850          66 : }
   13851             : 
   13852          27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   13853             :                                            const std::string &osName,
   13854          27 :                                            int nValue)
   13855             :     : GDALAbstractMDArray(osParentName, osName),
   13856             :       GDALAttribute(osParentName, osName),
   13857          27 :       m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
   13858             : {
   13859          27 : }
   13860             : 
   13861           7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   13862             :                                            const std::string &osName,
   13863           7 :                                            const std::vector<GUInt32> &anValues)
   13864             :     : GDALAbstractMDArray(osParentName, osName),
   13865             :       GDALAttribute(osParentName, osName),
   13866           7 :       m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
   13867             : {
   13868           7 :     m_dims.push_back(std::make_shared<GDALDimension>(
   13869          14 :         std::string(), "dim0", std::string(), std::string(),
   13870           7 :         m_anValuesUInt32.size()));
   13871           7 : }
   13872             : 
   13873             : const std::vector<std::shared_ptr<GDALDimension>> &
   13874          14 : GDALAttributeNumeric::GetDimensions() const
   13875             : {
   13876          14 :     return m_dims;
   13877             : }
   13878             : 
   13879           8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
   13880             : {
   13881           8 :     return m_dt;
   13882             : }
   13883             : 
   13884           4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
   13885             :                                  const size_t *count, const GInt64 *arrayStep,
   13886             :                                  const GPtrDiff_t *bufferStride,
   13887             :                                  const GDALExtendedDataType &bufferDataType,
   13888             :                                  void *pDstBuffer) const
   13889             : {
   13890           4 :     if (m_dims.empty())
   13891             :     {
   13892           3 :         if (m_dt.GetNumericDataType() == GDT_Float64)
   13893           0 :             GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
   13894             :                                             bufferDataType);
   13895             :         else
   13896             :         {
   13897           3 :             CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
   13898           3 :             GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
   13899             :                                             bufferDataType);
   13900             :         }
   13901             :     }
   13902             :     else
   13903             :     {
   13904           1 :         CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
   13905           1 :         GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
   13906          30 :         for (size_t i = 0; i < count[0]; ++i)
   13907             :         {
   13908          29 :             GDALExtendedDataType::CopyValue(
   13909          29 :                 &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
   13910          29 :                                                       i * arrayStep[0])],
   13911          29 :                 m_dt, pabyDstBuffer, bufferDataType);
   13912          29 :             pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
   13913             :         }
   13914             :     }
   13915           4 :     return true;
   13916             : }
   13917             : 
   13918         192 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
   13919             :     const std::string &osParentName, const std::string &osName,
   13920             :     const std::shared_ptr<GDALDimension> &poDim, double dfStart,
   13921         192 :     double dfIncrement, double dfOffsetInIncrement)
   13922             :     : GDALAbstractMDArray(osParentName, osName),
   13923             :       GDALMDArray(osParentName, osName), m_dfStart(dfStart),
   13924             :       m_dfIncrement(dfIncrement),
   13925         384 :       m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
   13926             : {
   13927         192 : }
   13928             : 
   13929         192 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
   13930             :     const std::string &osParentName, const std::string &osName,
   13931             :     const std::shared_ptr<GDALDimension> &poDim, double dfStart,
   13932             :     double dfIncrement, double dfOffsetInIncrement)
   13933             : {
   13934             :     auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
   13935         192 :         osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
   13936         192 :     poArray->SetSelf(poArray);
   13937         192 :     return poArray;
   13938             : }
   13939             : 
   13940             : const std::vector<std::shared_ptr<GDALDimension>> &
   13941         786 : GDALMDArrayRegularlySpaced::GetDimensions() const
   13942             : {
   13943         786 :     return m_dims;
   13944             : }
   13945             : 
   13946         316 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
   13947             : {
   13948         316 :     return m_dt;
   13949             : }
   13950             : 
   13951             : std::vector<std::shared_ptr<GDALAttribute>>
   13952           4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
   13953             : {
   13954           4 :     return m_attributes;
   13955             : }
   13956             : 
   13957           0 : void GDALMDArrayRegularlySpaced::AddAttribute(
   13958             :     const std::shared_ptr<GDALAttribute> &poAttr)
   13959             : {
   13960           0 :     m_attributes.emplace_back(poAttr);
   13961           0 : }
   13962             : 
   13963         188 : bool GDALMDArrayRegularlySpaced::IRead(
   13964             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
   13965             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
   13966             :     void *pDstBuffer) const
   13967             : {
   13968         188 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
   13969       14719 :     for (size_t i = 0; i < count[0]; i++)
   13970             :     {
   13971       14531 :         const double dfVal = m_dfStart + (arrayStartIdx[0] + i * arrayStep[0] +
   13972       14531 :                                           m_dfOffsetInIncrement) *
   13973       14531 :                                              m_dfIncrement;
   13974       14531 :         GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
   13975             :                                         bufferDataType);
   13976       14531 :         pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
   13977             :     }
   13978         188 :     return true;
   13979             : }
   13980             : 
   13981        2898 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
   13982             :     const std::string &osParentName, const std::string &osName,
   13983        2898 :     const std::string &osType, const std::string &osDirection, GUInt64 nSize)
   13984        2898 :     : GDALDimension(osParentName, osName, osType, osDirection, nSize)
   13985             : {
   13986        2898 : }
   13987             : 
   13988             : std::shared_ptr<GDALMDArray>
   13989         702 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
   13990             : {
   13991         702 :     return m_poIndexingVariable.lock();
   13992             : }
   13993             : 
   13994             : // cppcheck-suppress passedByValue
   13995         484 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
   13996             :     std::shared_ptr<GDALMDArray> poIndexingVariable)
   13997             : {
   13998         484 :     m_poIndexingVariable = poIndexingVariable;
   13999         484 :     return true;
   14000             : }
   14001             : 
   14002          33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
   14003             : {
   14004          33 :     m_nSize = nNewSize;
   14005          33 : }
   14006             : 
   14007             : /************************************************************************/
   14008             : /*                       GDALPamMultiDim::Private                       */
   14009             : /************************************************************************/
   14010             : 
   14011             : struct GDALPamMultiDim::Private
   14012             : {
   14013             :     std::string m_osFilename{};
   14014             :     std::string m_osPamFilename{};
   14015             : 
   14016             :     struct Statistics
   14017             :     {
   14018             :         bool bHasStats = false;
   14019             :         bool bApproxStats = false;
   14020             :         double dfMin = 0;
   14021             :         double dfMax = 0;
   14022             :         double dfMean = 0;
   14023             :         double dfStdDev = 0;
   14024             :         GUInt64 nValidCount = 0;
   14025             :     };
   14026             : 
   14027             :     struct ArrayInfo
   14028             :     {
   14029             :         std::shared_ptr<OGRSpatialReference> poSRS{};
   14030             :         // cppcheck-suppress unusedStructMember
   14031             :         Statistics stats{};
   14032             :     };
   14033             : 
   14034             :     typedef std::pair<std::string, std::string> NameContext;
   14035             :     std::map<NameContext, ArrayInfo> m_oMapArray{};
   14036             :     std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
   14037             :     bool m_bDirty = false;
   14038             :     bool m_bLoaded = false;
   14039             : };
   14040             : 
   14041             : /************************************************************************/
   14042             : /*                          GDALPamMultiDim                             */
   14043             : /************************************************************************/
   14044             : 
   14045        1368 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
   14046        1368 :     : d(new Private())
   14047             : {
   14048        1368 :     d->m_osFilename = osFilename;
   14049        1368 : }
   14050             : 
   14051             : /************************************************************************/
   14052             : /*                   GDALPamMultiDim::~GDALPamMultiDim()                */
   14053             : /************************************************************************/
   14054             : 
   14055        1368 : GDALPamMultiDim::~GDALPamMultiDim()
   14056             : {
   14057        1368 :     if (d->m_bDirty)
   14058          29 :         Save();
   14059        1368 : }
   14060             : 
   14061             : /************************************************************************/
   14062             : /*                          GDALPamMultiDim::Load()                     */
   14063             : /************************************************************************/
   14064             : 
   14065         101 : void GDALPamMultiDim::Load()
   14066             : {
   14067         101 :     if (d->m_bLoaded)
   14068          90 :         return;
   14069          44 :     d->m_bLoaded = true;
   14070             : 
   14071          44 :     const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
   14072          44 :     d->m_osPamFilename =
   14073          88 :         pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
   14074          44 :     CPLXMLTreeCloser oTree(nullptr);
   14075             :     {
   14076          88 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
   14077          44 :         oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
   14078             :     }
   14079          44 :     if (!oTree)
   14080             :     {
   14081          33 :         return;
   14082             :     }
   14083          11 :     const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
   14084          11 :     if (!poPAMMultiDim)
   14085           0 :         return;
   14086          35 :     for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
   14087          24 :          psIter = psIter->psNext)
   14088             :     {
   14089          24 :         if (psIter->eType == CXT_Element &&
   14090          24 :             strcmp(psIter->pszValue, "Array") == 0)
   14091             :         {
   14092          13 :             const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
   14093          13 :             if (!pszName)
   14094           0 :                 continue;
   14095          13 :             const char *pszContext = CPLGetXMLValue(psIter, "context", "");
   14096             :             const auto oKey =
   14097          26 :                 std::pair<std::string, std::string>(pszName, pszContext);
   14098             : 
   14099             :             /* --------------------------------------------------------------------
   14100             :              */
   14101             :             /*      Check for an SRS node. */
   14102             :             /* --------------------------------------------------------------------
   14103             :              */
   14104          13 :             const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
   14105          13 :             if (psSRSNode)
   14106             :             {
   14107             :                 std::shared_ptr<OGRSpatialReference> poSRS =
   14108           6 :                     std::make_shared<OGRSpatialReference>();
   14109           3 :                 poSRS->SetFromUserInput(
   14110             :                     CPLGetXMLValue(psSRSNode, nullptr, ""),
   14111             :                     OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
   14112           3 :                 const char *pszMapping = CPLGetXMLValue(
   14113             :                     psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
   14114           3 :                 if (pszMapping)
   14115             :                 {
   14116             :                     char **papszTokens =
   14117           3 :                         CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
   14118           6 :                     std::vector<int> anMapping;
   14119           9 :                     for (int i = 0; papszTokens && papszTokens[i]; i++)
   14120             :                     {
   14121           6 :                         anMapping.push_back(atoi(papszTokens[i]));
   14122             :                     }
   14123           3 :                     CSLDestroy(papszTokens);
   14124           3 :                     poSRS->SetDataAxisToSRSAxisMapping(anMapping);
   14125             :                 }
   14126             :                 else
   14127             :                 {
   14128           0 :                     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
   14129             :                 }
   14130             : 
   14131             :                 const char *pszCoordinateEpoch =
   14132           3 :                     CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
   14133           3 :                 if (pszCoordinateEpoch)
   14134           3 :                     poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
   14135             : 
   14136           3 :                 d->m_oMapArray[oKey].poSRS = std::move(poSRS);
   14137             :             }
   14138             : 
   14139             :             const CPLXMLNode *psStatistics =
   14140          13 :                 CPLGetXMLNode(psIter, "Statistics");
   14141          13 :             if (psStatistics)
   14142             :             {
   14143           7 :                 Private::Statistics sStats;
   14144           7 :                 sStats.bHasStats = true;
   14145           7 :                 sStats.bApproxStats = CPLTestBool(
   14146             :                     CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
   14147           7 :                 sStats.dfMin =
   14148           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
   14149           7 :                 sStats.dfMax =
   14150           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
   14151           7 :                 sStats.dfMean =
   14152           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
   14153           7 :                 sStats.dfStdDev =
   14154           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
   14155           7 :                 sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
   14156             :                     CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
   14157           7 :                 d->m_oMapArray[oKey].stats = sStats;
   14158          13 :             }
   14159             :         }
   14160             :         else
   14161             :         {
   14162          11 :             CPLXMLNode *psNextBackup = psIter->psNext;
   14163          11 :             psIter->psNext = nullptr;
   14164          11 :             d->m_apoOtherNodes.emplace_back(
   14165          11 :                 CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
   14166          11 :             psIter->psNext = psNextBackup;
   14167             :         }
   14168             :     }
   14169             : }
   14170             : 
   14171             : /************************************************************************/
   14172             : /*                          GDALPamMultiDim::Save()                     */
   14173             : /************************************************************************/
   14174             : 
   14175          29 : void GDALPamMultiDim::Save()
   14176             : {
   14177             :     CPLXMLTreeCloser oTree(
   14178          58 :         CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
   14179          33 :     for (const auto &poOtherNode : d->m_apoOtherNodes)
   14180             :     {
   14181           4 :         CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
   14182             :     }
   14183         108 :     for (const auto &kv : d->m_oMapArray)
   14184             :     {
   14185             :         CPLXMLNode *psArrayNode =
   14186          79 :             CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
   14187          79 :         CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
   14188          79 :         if (!kv.first.second.empty())
   14189             :         {
   14190           1 :             CPLAddXMLAttributeAndValue(psArrayNode, "context",
   14191             :                                        kv.first.second.c_str());
   14192             :         }
   14193          79 :         if (kv.second.poSRS)
   14194             :         {
   14195          71 :             char *pszWKT = nullptr;
   14196             :             {
   14197         142 :                 CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
   14198          71 :                 const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
   14199          71 :                 kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
   14200             :             }
   14201             :             CPLXMLNode *psSRSNode =
   14202          71 :                 CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
   14203          71 :             CPLFree(pszWKT);
   14204             :             const auto &mapping =
   14205          71 :                 kv.second.poSRS->GetDataAxisToSRSAxisMapping();
   14206         142 :             CPLString osMapping;
   14207         213 :             for (size_t i = 0; i < mapping.size(); ++i)
   14208             :             {
   14209         142 :                 if (!osMapping.empty())
   14210          71 :                     osMapping += ",";
   14211         142 :                 osMapping += CPLSPrintf("%d", mapping[i]);
   14212             :             }
   14213          71 :             CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
   14214             :                                        osMapping.c_str());
   14215             : 
   14216             :             const double dfCoordinateEpoch =
   14217          71 :                 kv.second.poSRS->GetCoordinateEpoch();
   14218          71 :             if (dfCoordinateEpoch > 0)
   14219             :             {
   14220             :                 std::string osCoordinateEpoch =
   14221           2 :                     CPLSPrintf("%f", dfCoordinateEpoch);
   14222           1 :                 if (osCoordinateEpoch.find('.') != std::string::npos)
   14223             :                 {
   14224           6 :                     while (osCoordinateEpoch.back() == '0')
   14225           5 :                         osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
   14226             :                 }
   14227           1 :                 CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
   14228             :                                            osCoordinateEpoch.c_str());
   14229             :             }
   14230             :         }
   14231             : 
   14232          79 :         if (kv.second.stats.bHasStats)
   14233             :         {
   14234             :             CPLXMLNode *psMDArray =
   14235           5 :                 CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
   14236           5 :             CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
   14237           5 :                                         kv.second.stats.bApproxStats ? "1"
   14238             :                                                                      : "0");
   14239           5 :             CPLCreateXMLElementAndValue(
   14240             :                 psMDArray, "Minimum",
   14241           5 :                 CPLSPrintf("%.17g", kv.second.stats.dfMin));
   14242           5 :             CPLCreateXMLElementAndValue(
   14243             :                 psMDArray, "Maximum",
   14244           5 :                 CPLSPrintf("%.17g", kv.second.stats.dfMax));
   14245           5 :             CPLCreateXMLElementAndValue(
   14246           5 :                 psMDArray, "Mean", CPLSPrintf("%.17g", kv.second.stats.dfMean));
   14247           5 :             CPLCreateXMLElementAndValue(
   14248             :                 psMDArray, "StdDev",
   14249           5 :                 CPLSPrintf("%.17g", kv.second.stats.dfStdDev));
   14250           5 :             CPLCreateXMLElementAndValue(
   14251             :                 psMDArray, "ValidSampleCount",
   14252           5 :                 CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
   14253             :         }
   14254             :     }
   14255             : 
   14256          58 :     std::vector<CPLErrorHandlerAccumulatorStruct> aoErrors;
   14257          29 :     CPLInstallErrorHandlerAccumulator(aoErrors);
   14258             :     const int bSaved =
   14259          29 :         CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
   14260          29 :     CPLUninstallErrorHandlerAccumulator();
   14261             : 
   14262          29 :     const char *pszNewPam = nullptr;
   14263          29 :     if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
   14264           0 :         ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
   14265             :     {
   14266           0 :         CPLErrorReset();
   14267           0 :         CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
   14268             :     }
   14269             :     else
   14270             :     {
   14271          29 :         for (const auto &oError : aoErrors)
   14272             :         {
   14273           0 :             CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
   14274             :         }
   14275             :     }
   14276          29 : }
   14277             : 
   14278             : /************************************************************************/
   14279             : /*                    GDALPamMultiDim::GetSpatialRef()                  */
   14280             : /************************************************************************/
   14281             : 
   14282             : std::shared_ptr<OGRSpatialReference>
   14283          10 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
   14284             :                                const std::string &osContext)
   14285             : {
   14286          10 :     Load();
   14287             :     auto oIter =
   14288          10 :         d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
   14289          10 :     if (oIter != d->m_oMapArray.end())
   14290           2 :         return oIter->second.poSRS;
   14291           8 :     return nullptr;
   14292             : }
   14293             : 
   14294             : /************************************************************************/
   14295             : /*                    GDALPamMultiDim::SetSpatialRef()                  */
   14296             : /************************************************************************/
   14297             : 
   14298          72 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
   14299             :                                     const std::string &osContext,
   14300             :                                     const OGRSpatialReference *poSRS)
   14301             : {
   14302          72 :     Load();
   14303          72 :     d->m_bDirty = true;
   14304          72 :     if (poSRS && !poSRS->IsEmpty())
   14305          71 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
   14306             :             poSRS->Clone());
   14307             :     else
   14308           2 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
   14309           1 :             .poSRS.reset();
   14310          72 : }
   14311             : 
   14312             : /************************************************************************/
   14313             : /*                           GetStatistics()                            */
   14314             : /************************************************************************/
   14315             : 
   14316          13 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
   14317             :                                       const std::string &osContext,
   14318             :                                       bool bApproxOK, double *pdfMin,
   14319             :                                       double *pdfMax, double *pdfMean,
   14320             :                                       double *pdfStdDev, GUInt64 *pnValidCount)
   14321             : {
   14322          13 :     Load();
   14323             :     auto oIter =
   14324          13 :         d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
   14325          13 :     if (oIter == d->m_oMapArray.end())
   14326           6 :         return CE_Failure;
   14327           7 :     const auto &stats = oIter->second.stats;
   14328           7 :     if (!stats.bHasStats)
   14329           1 :         return CE_Failure;
   14330           6 :     if (!bApproxOK && stats.bApproxStats)
   14331           0 :         return CE_Failure;
   14332           6 :     if (pdfMin)
   14333           6 :         *pdfMin = stats.dfMin;
   14334           6 :     if (pdfMax)
   14335           6 :         *pdfMax = stats.dfMax;
   14336           6 :     if (pdfMean)
   14337           6 :         *pdfMean = stats.dfMean;
   14338           6 :     if (pdfStdDev)
   14339           6 :         *pdfStdDev = stats.dfStdDev;
   14340           6 :     if (pnValidCount)
   14341           6 :         *pnValidCount = stats.nValidCount;
   14342           6 :     return CE_None;
   14343             : }
   14344             : 
   14345             : /************************************************************************/
   14346             : /*                           SetStatistics()                            */
   14347             : /************************************************************************/
   14348             : 
   14349           5 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
   14350             :                                     const std::string &osContext,
   14351             :                                     bool bApproxStats, double dfMin,
   14352             :                                     double dfMax, double dfMean,
   14353             :                                     double dfStdDev, GUInt64 nValidCount)
   14354             : {
   14355           5 :     Load();
   14356           5 :     d->m_bDirty = true;
   14357             :     auto &stats =
   14358           5 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
   14359           5 :     stats.bHasStats = true;
   14360           5 :     stats.bApproxStats = bApproxStats;
   14361           5 :     stats.dfMin = dfMin;
   14362           5 :     stats.dfMax = dfMax;
   14363           5 :     stats.dfMean = dfMean;
   14364           5 :     stats.dfStdDev = dfStdDev;
   14365           5 :     stats.nValidCount = nValidCount;
   14366           5 : }
   14367             : 
   14368             : /************************************************************************/
   14369             : /*                           ClearStatistics()                          */
   14370             : /************************************************************************/
   14371             : 
   14372           0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
   14373             :                                       const std::string &osContext)
   14374             : {
   14375           0 :     Load();
   14376           0 :     d->m_bDirty = true;
   14377           0 :     d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
   14378             :         false;
   14379           0 : }
   14380             : 
   14381             : /************************************************************************/
   14382             : /*                           ClearStatistics()                          */
   14383             : /************************************************************************/
   14384             : 
   14385           1 : void GDALPamMultiDim::ClearStatistics()
   14386             : {
   14387           1 :     Load();
   14388           1 :     d->m_bDirty = true;
   14389           3 :     for (auto &kv : d->m_oMapArray)
   14390           2 :         kv.second.stats.bHasStats = false;
   14391           1 : }
   14392             : 
   14393             : /************************************************************************/
   14394             : /*                             GetPAM()                                 */
   14395             : /************************************************************************/
   14396             : 
   14397             : /*static*/ std::shared_ptr<GDALPamMultiDim>
   14398         784 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
   14399             : {
   14400         784 :     auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
   14401         784 :     if (poPamArray)
   14402         563 :         return poPamArray->GetPAM();
   14403         221 :     return nullptr;
   14404             : }
   14405             : 
   14406             : /************************************************************************/
   14407             : /*                           GDALPamMDArray                             */
   14408             : /************************************************************************/
   14409             : 
   14410        3634 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
   14411             :                                const std::string &osName,
   14412             :                                const std::shared_ptr<GDALPamMultiDim> &poPam,
   14413           0 :                                const std::string &osContext)
   14414             :     :
   14415             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
   14416             :       GDALAbstractMDArray(osParentName, osName),
   14417             : #endif
   14418        3634 :       GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
   14419             : {
   14420        3634 : }
   14421             : 
   14422             : /************************************************************************/
   14423             : /*                    GDALPamMDArray::SetSpatialRef()                   */
   14424             : /************************************************************************/
   14425             : 
   14426          72 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
   14427             : {
   14428          72 :     if (!m_poPam)
   14429           0 :         return false;
   14430          72 :     m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
   14431          72 :     return true;
   14432             : }
   14433             : 
   14434             : /************************************************************************/
   14435             : /*                    GDALPamMDArray::GetSpatialRef()                   */
   14436             : /************************************************************************/
   14437             : 
   14438          10 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
   14439             : {
   14440          10 :     if (!m_poPam)
   14441           0 :         return nullptr;
   14442          10 :     return m_poPam->GetSpatialRef(GetFullName(), GetContext());
   14443             : }
   14444             : 
   14445             : /************************************************************************/
   14446             : /*                           GetStatistics()                            */
   14447             : /************************************************************************/
   14448             : 
   14449          13 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
   14450             :                                      double *pdfMin, double *pdfMax,
   14451             :                                      double *pdfMean, double *pdfStdDev,
   14452             :                                      GUInt64 *pnValidCount,
   14453             :                                      GDALProgressFunc pfnProgress,
   14454             :                                      void *pProgressData)
   14455             : {
   14456          13 :     if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
   14457             :                                           bApproxOK, pdfMin, pdfMax, pdfMean,
   14458          13 :                                           pdfStdDev, pnValidCount) == CE_None)
   14459             :     {
   14460           6 :         return CE_None;
   14461             :     }
   14462           7 :     if (!bForce)
   14463           4 :         return CE_Warning;
   14464             : 
   14465           3 :     return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
   14466             :                                       pdfMean, pdfStdDev, pnValidCount,
   14467           3 :                                       pfnProgress, pProgressData);
   14468             : }
   14469             : 
   14470             : /************************************************************************/
   14471             : /*                           SetStatistics()                            */
   14472             : /************************************************************************/
   14473             : 
   14474           5 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
   14475             :                                    double dfMax, double dfMean, double dfStdDev,
   14476             :                                    GUInt64 nValidCount,
   14477             :                                    CSLConstList /* papszOptions */)
   14478             : {
   14479           5 :     if (!m_poPam)
   14480           0 :         return false;
   14481           5 :     m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
   14482             :                            dfMax, dfMean, dfStdDev, nValidCount);
   14483           5 :     return true;
   14484             : }
   14485             : 
   14486             : /************************************************************************/
   14487             : /*                           ClearStatistics()                          */
   14488             : /************************************************************************/
   14489             : 
   14490           0 : void GDALPamMDArray::ClearStatistics()
   14491             : {
   14492           0 :     if (!m_poPam)
   14493           0 :         return;
   14494           0 :     m_poPam->ClearStatistics(GetFullName(), GetContext());
   14495             : }
   14496             : 
   14497             : //! @endcond

Generated by: LCOV version 1.14